Ruby on rails Rails计数器缓存未正确更新
使用Rails3.1.3,我试图弄明白为什么在通过update\u属性更改父记录id时,计数器缓存没有被正确更新Ruby on rails Rails计数器缓存未正确更新,ruby-on-rails,ruby-on-rails-3.1,rails-activerecord,Ruby On Rails,Ruby On Rails 3.1,Rails Activerecord,使用Rails3.1.3,我试图弄明白为什么在通过update\u属性更改父记录id时,计数器缓存没有被正确更新 class ExhibitorRegistration < ActiveRecord::Base belongs_to :event, :counter_cache => true end class Event < ActiveRecord::Base has_many :exhibitor_registrations, :dependent =>
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
我是否应该期望此功能正常工作,或者我是否需要手动跟踪更改并自己更新计数器?来自:
:计数器\u缓存
通过使用递增计数器
和递减计数器
在关联类上缓存所属对象的数量。当创建此类的对象时,计数器缓存将递增,而当销毁此类的对象时,计数器缓存将递减
当对象从一个所有者移动到另一个所有者时,没有提到更新缓存。当然,Rails文档通常是不完整的,因此我们必须查看源代码以进行确认。当您说:counter_cache=>true
时,您和:
回调,以调整计数器本身,类似这样(未测试的演示代码):
class ExhibitorRegistrationtrue
在\u保存:修复\u计数器\u缓存之前,:if=>->(er){!er.new\u record?&&er.event\u id\u changed?}
私有的
def fix_计数器_缓存
事件递减计数器(:参展商注册计数,自我事件id)
事件增量计数器(:参展商注册计数,自我事件id)
结束
结束
如果你喜欢冒险,你可以在ActiveRecord::Associations::Builder#add_counter_cache_callbacks
中添加类似的补丁并提交补丁。您所期望的行为是合理的,我认为ActiveRecord支持它是有意义的。计数器缓存功能设计为通过关联名称而不是基础id列工作。在您的测试中,而不是:
registration.update_attributes(:event_id => other_event.id)
试一试
更多信息可以在这里找到:我最近遇到了同样的问题(Rails 3.2.3)。看起来它还没有被修复,所以我不得不继续进行修复。下面是我如何修改ActiveRecord::Base并利用更新后回调来保持计数器缓存的同步
扩展ActiveRecord::Base
使用以下内容创建一个新文件lib/fix\u counters\u update.rb
:
module FixUpdateCounters
def fix_updated_counters
self.changes.each {|key, value|
# key should match /master_files_id/ or /bibls_id/
# value should be an array ['old value', 'new value']
if key =~ /_id/
changed_class = key.sub(/_id/, '')
changed_class.camelcase.constantize.decrement_counter(:"#{self.class.name.underscore.pluralize}_count", value[0]) unless value[0] == nil
changed_class.camelcase.constantize.increment_counter(:"#{self.class.name.underscore.pluralize}_count", value[1]) unless value[1] == nil
end
}
end
end
ActiveRecord::Base.send(:include, FixUpdateCounters)
上面的代码使用方法changes
,该方法返回一个包含已更改属性的哈希值以及一个旧值和新值的数组。通过测试属性以查看它是否是一种关系(即以/\u id/结尾),可以有条件地确定是否需要运行递减计数器
和/或递增计数器
。必须测试数组中是否存在nil
,否则将导致错误
添加到初始值设定项中
使用以下内容创建一个新文件config/initializers/active_record_extensions.rb
:
module FixUpdateCounters
def fix_updated_counters
self.changes.each {|key, value|
# key should match /master_files_id/ or /bibls_id/
# value should be an array ['old value', 'new value']
if key =~ /_id/
changed_class = key.sub(/_id/, '')
changed_class.camelcase.constantize.decrement_counter(:"#{self.class.name.underscore.pluralize}_count", value[0]) unless value[0] == nil
changed_class.camelcase.constantize.increment_counter(:"#{self.class.name.underscore.pluralize}_count", value[1]) unless value[1] == nil
end
}
end
end
ActiveRecord::Base.send(:include, FixUpdateCounters)
需要“修复更新计数器”
添加到模型中
对于要更新计数器缓存的每个型号,请添加回调:
class Comment < ActiveRecord::Base
after_update :fix_updated_counters
....
end
class注释
此问题的修复程序已合并到active record master中
如果您的计数器已损坏,或者您直接用SQL对其进行了修改,则可以修复它
使用:
ModelName.reset_counters(id_of_the_object_having_corrupted_count, one_or_many_counters)
示例1:重新计算id为17的帖子上的缓存计数。
Post.reset_counters(17, :comments)
Article.ids.each { |id| Article.reset_counters(id, :comments) }
示例2:重新计算所有文章的缓存计数。
Post.reset_counters(17, :comments)
Article.ids.each { |id| Article.reset_counters(id, :comments) }
这是行不通的,计数器更新只绑定到创建和销毁,这样它们就不会被更改触发。我只是仔细检查了一下,这是在通过ExhibitorRegistration实例上的update_属性修改活动时递增和递减cexhibitor_registrations_count列。我使用的是Rails 3.0.7如果你看一下ActiveRecord::Associations::Builder::BelongsTo#add_counter_cache_callbacks
你就会明白我在说什么。但是,您将在ActiveRecord::Associations::BelongsToAssociation\35; update\u计数器中看到更多的计数器摆弄。我想知道是否有两个独立的代码路径不太一致。@muistooshort当我开始尝试将补丁应用到Rails时,我看到了同样的情况。但是,我希望在控制器中保持update_属性工作,而不必显式查找事件并将其添加到参数中。我仍在研究如何正确地修补Rails,但我要过几天才能做出准确的评估。@MichaelGater:我的看法是,同一件事有两条不同的代码路径,而只有id的路径是不完整的。看起来像一个由两部分组成的bug:文档不正确,因为它没有说明“更改”案例,并且id-only路径不完整,因为它没有处理“更改”案例。谢谢@mu太短,这肯定解决了问题。我认为这在ActiveRecord本身确实值得注意,我会考虑提交一个补丁。@MichaelGuter:酷,别忘了在你的补丁中包括一个文档更新:)@MichaelGuter:你可能也想试试Ben的方法。我将再次检查Rails代码,看看是否遗漏了任何内容。这可能只是一个bug和糟糕/不完整的文档。@Pierre:还有lambda{| er |…}
。有时能帮上忙(但不总是)。这个答案帮了大忙。我还要提到,读者