Почему столбец 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

Почему это не происходит автоматически?


person Dane O'Connor    schedule 11.07.2013    source источник
comment
Что касается принятого ответа, я вижу такое же поведение с l.storables << Storable.last, что, вероятно, хуже.   -  person dgilperez    schedule 18.07.2013


Ответы (1)


Во-первых, я не думаю, что уместно писать что-то вроде l.storables << Storable.create.

При написании этого происходят две вещи:

  1. Storable.create создает новый объект Storeable с location_id nil

  2. l.storables << обновляет созданный объект, устанавливает location_id равным l.id и почему-то забывает обновить кэш счетчика.

Это может быть ошибка ActiveRecord, поскольку она должна была быть умнее, но на самом деле вы выполнили два SQL (вставить в хранимый и обновить хранимый набор location_id = что-то) просто для вставки новой сохраняемой записи. В любом случае это плохая идея, и если у вас есть ограничение внешнего ключа на location_id, первая вставка даже не удастся.

Так что используйте l.storables << Storable.new вместо этого

PS: с l.storables << Storable.create, поскольку возвращаемое значение Storable.create не является новой записью, l немного сложно решить, что делать. В некоторых случаях ему нужно увеличить свой собственный кэш счетчиков, в других случаях ему нужно увеличить свой собственный кэш счетчиков и уменьшить чужой кэш счетчиков, или ему может не потребоваться ничего делать.

person Dean Winchester    schedule 11.07.2013
comment
Жаль, что AR этого не понимает. Спасибо, что сузили проблему для меня! Я искал предыдущую работу по поддержке варианта использования «создать», и гем counter_culture выглядит так, как будто он может подойти. Этот тест, кажется, поддерживает именно этот случай: (github.com/ magnusvk/counter_culture/blob/master/spec/). - person Dane O'Connor; 12.07.2013