Using ActiveRecord, is there a way to get the old values of a record during after_update
Solution 1
Ditto what everyone is saying about transactions.
That said...
ActiveRecord as of Rails 2.1 keeps track of the attribute values of an object. So if you have an attribute total
, you will have a total_changed?
method and a total_was
method that returns the old value.
There's no need to add anything to your model to keep track of this anymore.
Update: Here is the documentation for ActiveModel::Dirty as requested.
Solution 2
Appending "_was" to your attribute will give you the previous value before saving the data.
These methods are called dirty methods methods.
Cheers!
Solution 3
Some other folks are mentioning wrapping all this in a transaction, but I think that's done for you; you just need to trigger the rollback by raising an exception for errors in the after_* callbacks.
See http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
The entire callback chain of a save, save!, or destroy call runs within a transaction. That includes after_* hooks. If everything goes fine a COMMIT is executed once the chain has been completed.
If a before_* callback cancels the action a ROLLBACK is issued. You can also trigger a ROLLBACK raising an exception in any of the callbacks, including after_* hooks. Note, however, that in that case the client needs to be aware of it because an ordinary save will raise such exception instead of quietly returning false.
Solution 4
To get all changed fields, with their old and new values respectively:
person = Person.create!(:name => 'Bill')
person.name = 'Bob'
person.save
person.changes # => {"name" => ["Bill", "Bob"]}
Solution 5
ActiveRecord::Dirty is a module that's built into ActiveRecord for tracking attribute changes. So you can use thing.amount_was
to get the old value.
Comments
-
Abel over 3 years
Setup using a simple example: I've got 1 table (
Totals
) that holds the sum of theamount
column of each record in a second table (Things
).When a
thing.amount
gets updated, I'd like to simply add the difference between the old value and the new value tototal.sum
.Right now I'm subtracting
self.amount
duringbefore_update
and addingself.amount
duringafter_update
. This places WAY too much trust in the update succeeding.Constraint: I don't want to simply recalculate the sum of all the transactions.
Question: Quite simply, I'd like to access the original value during an
after_update
callback. What ways have you come up with do this?Update: I'm going with Luke Francl's idea. During an
after_update
callback you still have access to theself.attr_was
values which is exactly what I wanted. I also decided to go with anafter_update
implementation because I want to keep this kind of logic in the model. This way, no matter how I decide to update transactions in the future, I'll know that I'm updating the sum of the transactions correctly. Thanks to everyone for your implementation suggestions. -
Abel about 15 yearsI'm struggling to see how i would implement what i want using a transaction. Does a transaction like this live in the model or in the controller? Do I remove my 'after_update' and 'before_update' callbacks? Finally, how do I get the old value that i need in order to determine the difference?
-
Abel about 15 yearsnevermind, i see that i have to place the code in the controller. we're good.
-
Jason Axelson over 7 yearsI believe the last line should now read
person.previous_changes
instead ofperson.changes