Rails counter_cache 未正确更新 [英] Rails counter_cache not updating correctly
问题描述
使用 Rails 3.1.3,我试图弄清楚为什么在通过 update_attributes 更改父记录 ID 时我们的计数器缓存没有被正确更新.
Using Rails 3.1.3 and I'm trying to figure out why our counter caches aren't being updated correctly when changing the parent record id via update_attributes.
class ExhibitorRegistration < ActiveRecord::Base
belongs_to :event, :counter_cache => true
end
class Event < ActiveRecord::Base
has_many :exhibitor_registrations, :dependent => :destroy
end
describe ExhibitorRegistration do
it 'correctly maintains the counter cache on events' do
event = Factory(:event)
other_event = Factory(:event)
registration = Factory(:exhibitor_registration, :event => event)
event.reload
event.exhibitor_registrations_count.should == 1
registration.update_attributes(:event_id => other_event.id)
event.reload
event.exhibitor_registrations_count.should == 0
other_event.reload
other_event.exhibitor_registrations_count.should == 1
end
end
此规范失败,表明事件的计数器缓存未递减.
This spec fails indicating that the counter cache on event is not being decremented.
1) ExhibitorRegistration correctly maintains the counter cache on events
Failure/Error: event.exhibitor_registrations_count.should == 0
expected: 0
got: 1 (using ==)
我是否应该期望这会起作用,还是我需要手动跟踪更改并自己更新计数器?
Should I even expect this to work or do I need to manually track the changes and update the counter myself?
推荐答案
来自 精美手册:
:counter_cache
通过使用increment_counter
和decrement_counter
缓存关联类上所属对象的数量.计数器缓存在此类对象创建时递增,销毁时递减.
Caches the number of belonging objects on the associate class through the use of increment_counter
and decrement_counter
. The counter cache is incremented when an object of this class is created and decremented when it’s destroyed.
当对象从一个所有者移动到另一个所有者时,没有提到更新缓存.当然,Rails 文档通常是不完整的,因此我们必须查看源以进行确认.当你说 :counter_cache =>真的
,你#Lreferr="relno触发对私有add_counter_cache_callbacks
方法 和add_counter_cache_callbacks
这样做:
There's no mention of updating the cache when an object is moved from one owner to another. Of course, the Rails documentation is often incomplete so we'll have to look at the source for confirmation. When you say :counter_cache => true
, you trigger a call to the private add_counter_cache_callbacks
method and add_counter_cache_callbacks
does this:
- 添加一个
after_create
回调,它调用increment_counter
. - 添加一个
before_destroy
回调,它调用decrement_counter
. - 调用
attr_readonly
将计数器列设为只读.
- Adds an
after_create
callback which callsincrement_counter
. - Adds an
before_destroy
callback which callsdecrement_counter
. - Calls
attr_readonly
to make the counter column readonly.
我认为您并没有期望太多,您只是期望 ActiveRecord 比现在更完整.
I don't think you're expecting too much, you're just expecting ActiveRecord to be more complete than it is.
虽然一切都没有丢失,您可以毫不费力地自己填补缺失的部分.如果您想允许重新父级并更新您的计数器,您可以将 before_save
回调添加到您的 ExhibitorRegistration 以调整计数器本身,如下所示(未经测试的演示代码):
All is not lost though, you can fill in the missing pieces yourself without too much effort. If you want to allow reparenting and have your counters updated, you can add a before_save
callback to your ExhibitorRegistration that adjusts the counters itself, something like this (untested demo code):
class ExhibitorRegistration < ActiveRecord::Base
belongs_to :event, :counter_cache => true
before_save :fix_counter_cache, :if => ->(er) { !er.new_record? && er.event_id_changed? }
private
def fix_counter_cache
Event.decrement_counter(:exhibitor_registration_count, self.event_id_was)
Event.increment_counter(:exhibitor_registration_count, self.event_id)
end
end
如果您喜欢冒险,可以将类似的内容修补到 ActiveRecord::Associations::Builder#add_counter_cache_callbacks
中并提交补丁.您期望的行为是合理的,我认为 ActiveRecord 支持它是有意义的.
If you were adventurous, you could patch something like that into ActiveRecord::Associations::Builder#add_counter_cache_callbacks
and submit a patch. The behavior you're expecting is reasonable and I think it would make sense for ActiveRecord to support it.
这篇关于Rails counter_cache 未正确更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!