3

次のモデルがあるとします。

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

これが自動的に行われないのはなぜですか?

4

1 に答える 1

3

一つには、 のような書き方は適切ではないと思いますl.storables << Storable.create

これを書くと、次の 2 つのことが起こります。

  1. Storable.createlocation_idnilで新しい Storable オブジェクトを作成します

  2. l.storables <<作成されたオブジェクトを更新し、location_id を に設定しl.id、どういうわけかカウンター キャッシュを更新するのを忘れています。

ActiveRecord の方がスマートなはずだったので、ActiveRecord のせいかもしれませんが、実際には 2 つの SQL (insert into storable & update storable set location_id = something) を実行して、新しい保存可能なレコードを挿入するだけです。とにかく、それは悪い考えです.location_idに外部キー制約がある場合、最初の挿入は失敗することさえあります.

l.storables << Storable.new代わりに使用してください

PS: ではl.storables << Storable.create、 の戻り値は新しいレコードではないため、何をすべきかを判断するStorable.createのは少し難しいです。l場合によっては、独自のカウンター キャッシュをインクリメントする必要があります。また、独自のカウンター キャッシュをインクリメントし、他のカウンター キャッシュをデクリメントする必要がある場合や、何もする必要がない場合もあります。

于 2013-07-11T09:13:09.447 に答える