Проблема с реализацией counter_cache
Грабли прерваны! ... posts_count помечается как ошибки только для чтения.
У меня есть две модели: пользователь и пост.
users has_many posts.
posts belongs_to :user, :counter_cache => true
У меня есть миграция, которая добавляет столбец posts_count в таблицу пользователей, а затем вычисляет и записывает текущее количество сообщений на пользователя.
self.up
add_column :users, :posts_count, :integer, :default => 0
User.reset_column_information
User.all.each do |u|
u.update_attribute( :posts_count, u.posts.count)
end
end
когда я запускаю миграцию, я получаю ошибку. Конечно, это довольно четко, и если я уберу объявление: counter_cache из модели posts, например
belongs_to :user
миграция проходит нормально. Это, очевидно, не имеет смысла, потому что вы не могли бы реализовать это таким образом. Что мне не хватает?
2 ответа
Вы должны использовать User.reset_counters
сделать это. Кроме того, я бы порекомендовал использовать find_each
вместо each
потому что он будет повторять коллекцию в пакетах, а не все сразу.
self.up
add_column :users, :posts_count, :integer, :default => 0
User.reset_column_information
User.find_each do |u|
User.reset_counters u.id, :posts
end
end
ОК, документация гласит:
Столбцы кэша счетчика добавляются в список доступных только для чтения атрибутов содержащейся модели с помощью attr_readonly.
Я думаю, что так и происходит: вы объявляете счетчик в определении модели, тем самым предоставляя атрибут "posts_count" только для чтения. Затем в процессе миграции вы пытаетесь обновить его напрямую, что приводит к указанной вами ошибке.
Быстрое и грязное решение состоит в том, чтобы удалить объявление counter_cache из модели, запустить миграцию (чтобы добавить необходимый столбец в базу данных и заполнить его счетчиками текущих сообщений), а затем повторно добавить объявление counter_cache в модель. Должно работать, но противно и требует ручного вмешательства во время миграции - не очень хорошая идея.
Я нашел этот пост в блоге, в котором предлагается изменить список атрибутов модели только для чтения во время миграции, он немного устарел, но вы можете попробовать.