我正在批量导入记录,并且不希望每次都更新计数器。我想在批量upsert期间跳过counter_cache sql更新,然后在循环结束时调用reset_counters。
我试过了:
my_model = MyModel.find_or_initialize_by_slug row[:slug]
my_model.association(:my_association).reflection.options[:counter_cache] = false
my_model.attributes = {:name => "Christopher"}
my_model.save!
但我可以在sql输出中看到它仍在更新counter_cache。
注意:我无法使用activerecord-import,因为我想执行upserts而我正在使用postgresql
答案 0 :(得分:5)
您可以使用几种不同的选项来跳过计数器缓存的更新,而您选择的更新实际上取决于您希望如何构建应用程序。我将讨论您可以绕过计数器缓存的不同方法,并提及您可能想要做的一些注意事项。
基本上,有三种不同的方法可以跳过计数器缓存的更新:
请注意,上面的前两个选项通常与在ActiveRecord中禁用回调相关,这是有道理的,因为计数器缓存是通过回调在内部实现的。
当Rails加载与计数器缓存有关联的模型时,它会动态定义回调方法。如果要将这些作为回调禁用,则必须先弄清楚回调名称是什么。
有两种主要方法可以确定Rails为实现这些回调而定义的方法。您可以阅读Rails源代码以找出它将通过String intorpolation生成的名称,或者您可以使用内省来确定您的类响应的方法。我将举例说明如何使用内省来找出ActiveRecord定义的回调,以便自动实现计数器缓存。
假设你有一个名为SpecialReply的类,它来自一个来自ActiveRecord :: Base(this example comes from the test suite with Rails)的Reply类。它有一个计数器缓存列,定义如下:
class SpecialReply < ::Reply
belongs_to :special_topic, :foreign_key => 'parent_id', :counter_cache => 'replies_count'
end
在控制台中,您可以使用.methods
查看您的课程响应的方法。这会产生很多噪音,因为Object
的每个实例都已经响应了很多方法,所以你可以按如下方式缩小列表范围:
1.9.3-p194 :001 > sr = SpecialReply.new
1.9.3-p194 :002 > sr.methods - Object.methods
在你说的第二行,告诉我我的SpecialReply实例响应的所有方法,减去所有对象响应的方法。这通常会通过过滤掉您正在查看的类类型并不特定的方法来帮助进行内省。
不幸的是,即使在此过滤之后,由于ActiveRecord添加到其所有后代类的方法,也会产生很多噪音。在这种情况下,grep
很有用 - 因为ActiveRecord有助于创建包含字符串counter_cache
(see the meta-programming used by ActiveRecord to generate a counter cache method for a belongs_to
association)的计数器回调方法,您可以找到与计数器缓存相关的回调,具体如下: / p>
1.9.3-p194 :001 > sr = SpecialReply.new
1.9.3-p194 :002 > sr.methods.map(&:to_s).grep(/counter_cache/)
请注意,由于grep
对字符串进行操作,而methods
返回符号方法名称数组,因此我们首先使用to_proc
(&:
)进行转换所有符号到字符串,然后grep out包含counter_cache
。这让我看到以下方法,似乎它们可能是由ActiveRecord自动生成的,作为实现计数器缓存的回调:
belongs_to_counter_cache_after_create_for_special_topic
belongs_to_counter_cache_before_destroy_for_special_topic
belongs_to_counter_cache_after_create_for_topic
belongs_to_counter_cache_before_destroy_for_topic
belongs_to_counter_cache_after_create_for_topic_with_primary_key
belongs_to_counter_cache_before_destroy_for_topic_with_primary_key
您应该能够在程序中执行类似的过程,以确定ActiveRecord添加的方法名称,以便您可以在existing instructions for removing callbacks之后将其删除。
您从上述选项中的选择实际上取决于您的程序结构,以及您为提高加载数据效率而愿意考虑的权衡。值得注意的是,前两个选项可以通过从外部修改类的行为(猴子修补)来降低代码的可读性,并且可以通过绕过数据更新的业务规则(缓存列的更新)来使系统不稳定。出于这些原因,我会考虑是否可以创建另一个类来以优化的方式加载数据,同时最大限度地减少对可读性或数据一致性的影响。