web-dev-qa-db-ja.com

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

簡単な例を使用した設定: 1つのテーブル(Totals)があり、2番目のテーブルの各レコードのamount列の合計を保持しています(Things)。

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

現在、self.amount中にbefore_updateを減算し、self.amount中にafter_updateを追加しています。これにより、更新の成功に信頼が寄せられます。

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

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

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

73
Abel

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

とはいえ...

Rails 2.1の時点でのActiveRecordは、オブジェクトの属性値を追跡します。したがって、属性totalがあれば、total_changed?メソッドとtotal_was古い値を返すメソッド。

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

Update:以下は、要求に応じて ActiveModel :: Dirty のドキュメントです。

141
Luke Francl

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

これらのメソッドは dirty methods メソッドと呼ばれます。

乾杯!

10

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

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

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

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

9
Gabe Hollombe

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

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

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

4
John Topley

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

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

次に、after_updateコードで@old_amountを使用します。

3

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

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

0
jonnii

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

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

0
bradheintz