2010-11-28 12 views
42

私は「レーキが中止されました! ... posts_countには読み取り専用のエラーがあります。counter_cache実装の問題

私は2つのモデルを持っています:ユーザーと投稿。

users has_many posts. 

posts belongs_to :user, :counter_cache => true 

posts_count列をusers表に追加し、ユーザーごとの現在のポスト数を計算して記録する移行があります。

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 

移行が正常に実行されます。これは明らかに、あなたが実際にこのように実装することができなかったので意味がありません。私は何が欠けていますか?

答えて

97

これを行うには、User.reset_countersを使用する必要があります。さらに、eachの代わりにfind_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 
+2

"User.reset_counters u.id、:posts_count"というアプローチが好ましいと思われます。 http://api.rubyonrails.org/classes/ActiveRecord/CounterCache.html –

+0

上記を軽く訂正してください(そのコメントはもう編集できません): "User.reset_counters u。id:、posts " –

+0

@Mike Fischer良いキャッチは、' reset_counters'がSQL呼び出しを使用していないようです。 –

3

OK、ドキュメントの状態:

カウンターキャッシュカラムはATTR_READONLYを通じて、読み取り専用 属性の 含むモデルのリストに追加されます。

これは、モデルの定義でカウンタを宣言して、 "posts_count"属性を読み取り専用にすると思います。次に、マイグレーション中に直接更新しようとすると、エラーが発生します。

すぐに解決できるのは、モデルからcounter_cache宣言を削除し、移行を実行して(データベースに必要な列を追加し、それを現在のポストカウントで設定するため)、次に再追加しますモデルへのcounter_cache宣言。動作しなければならないが、厄介で、移行中に手作業で介入する必要があります。良い考えではありません。

私はthis blog postを見つけました。これは、移行中にモデルの読み取り専用属性のリストを変更することを示唆しています。少し浸出していますが、試してみてください。

+0

ご協力いただきありがとうございます。私はあなたが提案した回避策を使用していますが、同意すると、物事を生産に持ち込むための良い方法ではありません。おかげさまでブログ記事もありましたが、ハックはもはや機能していないようです(私は現在レール3.0.1を使用しています)。自分のキャッシュカウンタをロールアップしてアプリ内で管理しなければならないかもしれません。 –

+1

そのブログ記事は誤解を招くか、時代遅れです。 [this one](http://josh.the-owens.com/rails-edge-change-how-to-add-a-counter-cache)を参照してください。 –

+0

これは私の問題でした。 'counter_cache:true'はシーンの背後にある' attr_readonly'という魔法の背後にいくつかのことを設定していて、マイグレーションをブロックしています – Meltemi