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。
これでうまく動くようになりました。