Detecting if value of attribute changed during last update doesnt work with Active Model Dirty

11,007

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.

Share:
11,007
David Geismar
Author by

David Geismar

I started web development 3 months ago. I wish to become a rails ninja. Yahuuuu

Updated on June 17, 2022

Comments

  • David Geismar
    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 ?