Проблема с реализацией 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 из модели сообщений, например.

belongs_to :user

миграция проходит нормально. Это, очевидно, не имеет смысла, потому что вы не можете реализовать это таким образом. Что мне не хватает?


person NJ.    schedule 28.11.2010    source источник


Ответы (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
person Adam Lassek    schedule 28.11.2010
comment
Похоже, предпочтительный подход: User.reset_counters u.id, :posts_count. См. api.rubyonrails.org/classes/ActiveRecord/CounterCache.html. - person Mike Fischer; 21.01.2011
comment
Небольшое исправление к приведенному выше (больше не может редактировать этот комментарий): User.reset_counters u.id, :posts - person Mike Fischer; 21.01.2011
comment
@Mike Fischer хороший улов, похоже, reset_counters использует меньше вызовов SQL. - person Adam Lassek; 21.01.2011
comment
@gwho Я не вижу никаких уведомлений об устаревании ни для одного из методов ни в 4-1-stable, ни в master. Что заставляет вас думать, что? github.com/rails/rails/blob/master/ активная запись/библиотека/ - person Adam Lassek; 18.09.2014
comment
это сказал апидок. И это не работало для меня. я ошибаюсь - person ahnbizcad; 18.09.2014
comment
@gwho apidock называет все устаревшим, если код перемещает местоположения. Обычно я избегаю этого сайта, он часто сбивает с толку. - person Adam Lassek; 24.09.2014

Хорошо, в документации указано:

Столбцы кэша счетчиков добавляются в список атрибутов только для чтения содержащей модели через attr_readonly.

Я думаю, что происходит вот что: вы объявляете счетчик в определении модели, тем самым делая атрибут «posts_count» доступным только для чтения. Затем при миграции вы пытаетесь обновить его напрямую, что приводит к упомянутой вами ошибке.

Быстрое и грязное решение состоит в том, чтобы удалить объявление counter_cache из модели, запустить миграцию (чтобы добавить требуемый столбец в базу данных И заполнить его текущими счетчиками сообщений), а затем повторно добавить объявление counter_cache в модель. Должен работать, но неприятный и требует ручного вмешательства во время миграции - не очень хорошая идея.

Я нашел эту запись в блоге, в которой предлагается изменить список моделей атрибутов только для чтения во время миграции, он немного устарел, но вы можете попробовать.

person Roadmaster    schedule 28.11.2010
comment
Я ценю вашу помощь. Я использую предложенный вами обходной путь, но, как вы согласны, это не лучший способ запускать вещи в производство. Также спасибо за статью в блоге, но этот хак, похоже, больше не работает (в настоящее время я использую rails 3.0.1). Возможно, мне просто придется свернуть свои собственные счетчики кеша и поддерживать их в приложении. - person NJ.; 28.11.2010
comment
Этот пост в блоге вводит в заблуждение или устарел. См. это. - person Adam Lassek; 28.11.2010
comment
Это было проблемой для меня. counter_cache: true собирался делать какие-то закулисные attr_readonly магические действия за кулисами и блокировал мою миграцию - person Meltemi; 28.08.2013
comment
Этот способ создает проблему при попытке сделать это в удаленном репозитории, таком как heroku. Загрузить версию с отключенным контркешем, затем выполнить миграцию, а затем загрузить версию с реализованным контркешем? фу. - person ahnbizcad; 30.09.2014