跳过rails counter_cache更新

时间:2017-06-10 01:24:53

标签: ruby-on-rails ruby ruby-on-rails-4 counter-cache

我有一个使用rails'内置的counter_cache关联来增加/减少计数。我有一个要求,当我为特定情况销毁模型时,我需要禁用它。我试图做Model.skip_callback(:destroy, :belongs_to_counter_cache_after_update)之类的事情,但它似乎没有按预期工作(即它仍然最终减少相关模型)。任何有用的指示将不胜感激。

2 个答案:

答案 0 :(得分:0)

您可以创建一个标志来决定何时应该运行回调,例如:

class YourModel
  attr_accessor :skip_counter_cache_update

  def decrement_callback
    return if @skip_counter_cache_update
    # Run callback to decrement counter cache
    ...
  end  
end

因此,在销毁模型的对象之前,只需设置skip_counter_cache_update的值:

@object = YourModel.find(some_id)
@object.skip_counter_cache_update = true
@object.destroy

所以它不会运行递减回调。

答案 1 :(得分:0)

一种选择是临时覆盖负责在销毁时更新缓存计数的方法。 例如,如果您有以下两个模型

class Category < ActiveRecord::Base
  has_many :products
end

class Product < ActiveRecord::Base
  belongs_to :category, counter_cache: true
end

现在,您可以尝试使用以下

查找负责更新缓存计数的方法
2.1.5 :038 > Product.new.methods.map(&:to_s).grep(/counter_cache/)

这显示了与counter_cache相关的所有产品实例方法,结果如下

=> ["belongs_to_counter_cache_before_destroy_for_category", "belongs_to_counter_cache_after_create_for_category", "belongs_to_counter_cache_after_update_for_category"]

从显示的方法名称

"belongs_to_counter_cache_after_create_for_category"

可能负责销毁后的计数器缓存更新。 所以我决定暂时使用一个没有做任何事情的假方法覆盖这个方法(跳过计数器缓存更新)

Product.class_eval do
  def fake_belongs_to_counter_cache_before_destroy_for_category; end
  alias_method :real_belongs_to_counter_cache_before_destroy_for_category, :belongs_to_counter_cache_before_destroy_for_category
  alias_method :belongs_to_counter_cache_before_destroy_for_category, :fake_belongs_to_counter_cache_before_destroy_for_category
end

现在,如果您要销毁任何产品对象,它将不会更新Category表中的计数器缓存。 但是,在运行代码以销毁特定对象后,恢复实际方法非常重要。要恢复到实际的类方法,您可以执行以下操作

Product.class_eval do
  alias_method :belongs_to_counter_cache_before_destroy_for_category, :real_belongs_to_counter_cache_before_destroy_for_category
  remove_method :real_belongs_to_counter_cache_before_destroy_for_category
  remove_method :fake_belongs_to_counter_cache_before_destroy_for_category
end

为了确保在特定的销毁任务之后始终恢复方法定义,您可以编写一个类方法,确保同时运行覆盖和恢复代码

class Product < ActiveRecord::Base
  belongs_to :category, counter_cache: true

  def self.without_counter_cache_update_on_destroy(&block)
    self.class_eval do
      def fake_belongs_to_counter_cache_before_destroy_for_category; end
        alias_method :real_belongs_to_counter_cache_before_destroy_for_category, :belongs_to_counter_cache_before_destroy_for_category
        alias_method :belongs_to_counter_cache_before_destroy_for_category, :fake_belongs_to_counter_cache_before_destroy_for_category
    end
    yield
    self.class_eval do
      alias_method :belongs_to_counter_cache_before_destroy_for_category, :real_belongs_to_counter_cache_before_destroy_for_category
      remove_method :real_belongs_to_counter_cache_before_destroy_for_category
      remove_method :fake_belongs_to_counter_cache_before_destroy_for_category
    end
  end
end

现在,如果您销毁以下

中的任何产品对象
Product.without_counter_cache_update_on_destroy { Product.last.destroy }

它不会更新Category表中的计数器缓存。

参考文献:
禁用ActiveModel回调https://jeffkreeftmeijer.com/2010/disabling-activemodel-callbacks/ 临时覆盖方法:https://gist.github.com/aeden/1069124