Detecting if value of attribute changed during last update doesnt work with Active Model Dirty
Solution 1
.update
also saves the model after updating it's data, therefore resetting the dirty-values. Try using .assign_attributes
. It will just assign the attributes, then you can check for changes, and finally remember to save the model.
Solution 2
As @Thounder pointed out, the ActiveModel::Dirty
method <attribute>_changed?
is reset whenever you save a record. Thus, it only tracks changes between saves.
For your use case, what you want to use is the previous_changes
method, which returns a hash with the key being the attribute changed and the value being an array of 2 values: old and new.
person = Person.new(name: "Bob")
person.name_changed? # => true
person.save
person.name_changed? # => false (reset when save called)
person.previous_changes # => { name: [nil, "Bob"] }
person.previous_changes[:name] # => returns a "truthy" statement if :name attribute changed
My pseudo-code may be wrong, but the principle works. I've been bitten by this "gotcha" before, and I wish the Rails core team would change it.
I understand their reasoning, but it makes more sense to me to track <attribute>_changed?
after a save as well, because that seems the common use case to me.
Solution 3
You can try this method to check the changed attributes for the active record.
@partnership.changed.include?("status")
If it returns true then we have status attribute which was changed in this record.
Solution 4
Use @partnership.saved_change_to_status?
or @partnership.saved_change_to_attribute(:status)
as per docs.
David Geismar
I started web development 3 months ago. I wish to become a rails ninja. Yahuuuu
Updated on June 17, 2022Comments
-
David Geismar almost 2 years
I am trying to send a notification email in my rails app only if the value of my column status was modified by the current update. I tried using Active Model Dirty as was suggested in some post and the
status_changed?
method. Unfortunately my email is never sent because @partnership.status_changed? constantly returns false even though the value of status was indeed changed during the last update. Here's my controller code :def update authorize @partnership if @partnership.update(partnership_params) send_notification_email render json: {success: "partnership successfully updated"}, status: 200 else render_error(nil, @partnership) end end private def send_notification_email PartnershipMailer.partnership_status_change(@partnership).deliver_now if @partnership.status_changed? end
I have also included Active Model Dirty in my model :
class Partnership < ActiveRecord::Base include ActiveModel::Dirty
What am I doing wrong ?