Railsで既存のテーブルにてcouter_cacheを使うように修正する方法

Railsにはcounter_cacheというリレーション先のカウント数をキャッシュする仕組みがあります。これを使うと、リレーション先を参照してカウントせずに済むのでデータベースへのアクセス数が減って速くなります。いいですね〜。

で、これを使いたいと思うようになったので、入れてみました。当初、マイグレーション後の初期化方法を知らなかったので、とりあえずupdate_columnを使って更新していました。

class AddCommentsCountToPost < ActiveRecord::Migration
  def change
    add_column :posts, :comments_count, :integer, default: 0
    Post.find_each do |post|
      post.update_column :comments_count, post.comments.count
    end
  end
end

これでrake db:migrateした後、couter_cacheを使うように設定していました。

class Comment < ActiveRecord::Base
  belongs_to :post, counter_cache: true
end

その後、機能を作り終えてからレビュー用の環境にデプロイしたところ、なんと失敗!?

comments_count is marked as readonly

どうもcounter_cacheの設定を行うと読み込み専用の属性になるようで、update_columnでは更新できないと…。うーむ、設定する順番が悪かったのか…。となるとどうやって初期値を設定するのだろうか?と思ってぐぐったらやっぱりありました。

http://apidock.com/rails/ActiveRecord/Base/reset_counters/class

Post.reset_counters(id, :comments)

マイグレーションファイルを修正し直します。rake db:rollbackを先にやることをお忘れなく。

class AddCommentsCountToPost < ActiveRecord::Migration
  def up
    add_column :posts, :comments_count, :integer, default: 0
    Post.all.pluck(:id).each {|id| Post.reset_counters(id, :comments)}
  end

  def down
    remove_column :posts, :comments_count, :integer, default: 0
  end
end

そしてrake db:migrate。
これでうまく動くようになりました。


カテゴリー Ruby, Ruby on Rails | タグ | パーマリンク

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です