为什么当<<时,counter_cache列不会增加一个协会?

时间:2013-07-11 00:28:28

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

鉴于我有以下型号:

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

我对 counter_cache半工作感到困惑。我也无法发现配置问题。

在此项目中使用 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

为什么不自动发生这种情况?

1 个答案:

答案 0 :(得分:3)

首先,我不认为写l.storables << Storable.create之类的东西是合适的。

通过写这个,发生了两件事:

  1. Storable.create使用location_id nil创建一个新的可存储对象

  2. l.storables <<更新创建的对象,将location_id设置为l.id,并以某种方式忘记更新计数器缓存。

  3. 这可能是ActiveRecord的错,因为它本来应该更聪明,但你实际上只是为了插入一个新的可存储记录而执行了两个SQL(插入可存储和更新可存储的set location_id = something)。无论如何,这是一个坏主意,如果你对location_id有一个外键约束,第一个插入甚至会失败。

    所以请改用l.storables << Storable.new

    PS:使用l.storables << Storable.create,因为Storable.create的返回值不是新记录,l决定做什么有点困难。在某些情况下,它需要递增自己的计数器缓存,在其他情况下,它需要递增自己的计数器缓存并减少其他人的计数器缓存,否则它可能不需要做任何事情。