ActiveRecordを使用して、after_update中にレコードの古い値を取得する方法はありますか

StackOverflow https://stackoverflow.com/questions/607069

質問

簡単な例を使用したセットアップ:各レコードの amount 列の合計を保持する1つのテーブル( Totals )があります2番目のテーブル( Things )。

thing.amount が更新されたら、古い値と新しい値の差を total.sum に単純に追加したいと思います。

今、 before_update self.amount を差し引き、 after_update self.amount を追加しています。これにより、更新の成功に信頼が寄せられます。

制約:すべてのトランザクションの合計を単純に再計算したくありません。

質問:簡単に言えば、 after_update コールバック中に元の値にアクセスしたいのです。これをどのように思いつきましたか?

更新: Luke Franclのアイデアを採用します。 after_update コールバックの間、 self.attr_was の値にアクセスできますが、これはまさに私が望んでいたものです。また、この種のロジックをモデルに保持したいので、 after_update の実装を使用することにしました。これにより、将来どのようにトランザクションを更新することにしたとしても、トランザクションの合計を正しく更新していることがわかります。実装の提案に感謝します。

役に立ちましたか?

解決

トランザクションについて誰もが言っていることと同じ。

それは...

Rails 2.1以降のActiveRecordは、オブジェクトの属性値を追跡します。したがって、属性 total がある場合、 total_changed?メソッドと、古い値を返す total_was メソッドがあります。

これを追跡するためにモデルに何かを追加する必要はもうありません。

更新:以下は、 ActiveModel :: Dirtyのドキュメントです。 要求どおり。

他のヒント

これをすべてトランザクションでラッピングすることに言及している人もいますが、それはあなたのためにできたと思います。 after_ *コールバックでエラーの例外を発生させることにより、ロールバックをトリガーする必要があります。

http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.htmlを参照してください。

  

save、save !、またはdestroy呼び出しのコールバックチェーン全体がトランザクション内で実行されます。これにはafter_ *フックが含まれます。すべてがうまくいけば、チェーンが完了するとCOMMITが実行されます。

     

before_ *コールバックがアクションをキャンセルすると、ROLLBACKが発行されます。また、after_ *フックを含む任意のコールバックで例外を発生させるROLLBACKをトリガーできます。ただし、その場合、通常の保存ではfalseを静かに返すのではなく、このような例外が発生するため、クライアントはそれを認識する必要があります。

" _was"を追加属性に追加すると、データを保存する前に以前の値が得られます。

これらのメソッドは、ダーティーメソッドメソッドと呼ばれます。

乾杯!

変更されたすべてのフィールドを、それぞれ古い値と新しい値で取得するには:

person = Person.create!(:name => 'Bill')
person.name = 'Bob'
person.save
person.changes        # => {"name" => ["Bill", "Bob"]}

ActiveRecord :: Dirty は、追跡のためにActiveRecordに組み込まれているモジュールです属性の変更。したがって、 thing.amount_was を使用して古い値を取得できます。

これをモデルに追加します:

def amount=(new_value)
    @old_amount = read_attribute(:amount)
    write_attribute(:amount,new_value)
end

その後、after_updateコードで@old_amountを使用します。

まず、データを一緒に書き込むために、トランザクションでこれを行う必要があります。

質問に答えるには、before_updateでメンバー変数を古い値に設定するだけで、after_updateでアクセスできますが、これは非常にエレガントなソリューションではありません。

アイデア1:更新がデータベーストランザクションでラップされるため、更新が失敗してもTotalsテーブルは変更されません。 ActiveRecord Transactionsドキュメント

アイデア2:before_update中に@old_totalの古い値を隠します。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top