假设我有以下模型:
class Location < Active::Record
has_many :storables, foreign_key: :bin_id
# ...
end
class Storable < Active::Record
belongs_to :bin, class_name: :Location, counter_cache: true
# ...
end
当我运行以下规范时,counter_cache
的增量不正确。方法#1
和#2
按预期工作,但不是#3
。什么东西?
describe "location storables" do
specify "adding a storable increments the counter cache" do
l = Location.create
l.storables_count.should == 0 #=> PASSES
# method 1
s = Storable.create(bin: l)
l.reload
l.storables_count.should == 1 #=> PASSES
# method 2
l.storables.create
l.reload
l.storables_count.should == 2 #=> PASSES
# method 3
l.storables << Storable.create
l.reload
l.storables_count.should == 3 #=> FAILS, got 2 not 3
end
end
我真的被计数器_缓存半工作弄糊涂了。我也找不到配置问题。
在本项目中使用Rails 3.2.12。
更新
升级到rails 4没有帮助。此外,如果我将方法#3更改为以下,则测试通过:
# method 3
l.storables << Storable.create
puts "proxy : #{l.storables.count}" #=> 3
puts "relation : #{Storable.count}" #=> 3
puts "cache : #{l.storables_count}" #=> 2
Location.reset_counters(l.id, :storables) # corrects cache
l.reload
l.storables_count.should == 3 #=> PASSES
为什么这不是自动发生的?
首先,我认为写l.storables << Storable.create
这样的东西是不合适的。
通过写这篇文章,发生了两件事:
-
Storable.create
使用location_id
nil 创建新的可存储对象 -
l.storables <<
更新创建的对象,将location_id设置为l.id
,并且不知何故忘记更新计数器缓存。
这可能是ActiveRecord的错,因为它本应该更智能,但实际上您已经执行了两个SQL(insert into storable&update storable set location_id=something),只是为了插入一个新的可存储记录。无论如何,这是个坏主意,如果您对location_id有外键约束,第一次插入甚至会失败。
所以用l.storables << Storable.new
代替
PS:对于l.storables << Storable.create
,由于Storable.create
的返回值不是新记录,所以l
很难决定该怎么做。在某些情况下,它需要增加自己的计数器缓存,在其他情况下,需要增加自己计数器缓存并减少其他人的计算器缓存,或者它可能什么都不需要。