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