Rails 3 check if attribute changed

2019-01-08 05:16发布

问题:

Need to check if a block of attributes has changed before update in Rails 3.

street1, street2, city, state, zipcode

I know I could use something like

if @user.street1 != params[:user][:street1]
  then do something....
end

But that piece of code will be REALLY long. Is there a cleaner way?

回答1:

Check out ActiveModel::Dirty (available on all models by default). The documentation is really good, but it lets you do things such as:

@user.street1_changed? # => true/false


回答2:

This is how I solved the problem of checking for changes in multiple attributes.

attrs = ["street1", "street2", "city", "state", "zipcode"]

if (@user.changed & attrs).any?
  then do something....
end

The changed method returns an array of the attributes changed for that object.

Both @user.changed and attrs are arrays so I can get the intersection (see ary & other ary method). The result of the intersection is an array. By calling any? on the array, I get true if there is at least one intersection.

Also very useful, the changed_attributes method returns a hash of the attributes with their original values and the changes returns a hash of the attributes with their original and new values (in an array).

You can check APIDock for which versions supported these methods.

http://apidock.com/rails/ActiveModel/Dirty



回答3:

ActiveModel::Dirty didn't work for me because the @model.update_attributes() hid the changes. So this is how I detected changes it in an update method in a controller:

def update
  @model = Model.find(params[:id])
  detect_changes

  if @model.update_attributes(params[:model])
    do_stuff if attr_changed?
  end
end

private

def detect_changes
  @changed = []
  @changed << :attr if @model.attr != params[:model][:attr]
end

def attr_changed?
  @changed.include :attr
end

If you're trying to detect a lot of attribute changes it could get messy though. Probably shouldn't do this in a controller, but meh.



回答4:

Above answers are better but yet for knowledge we have another approch as well, Lets 'catagory' column value changed for an object (@design),

@design.changes.has_key?('catagory')

The .changes will return a hash with key as column's name and values as a array with two values [old_value, new_value] for each columns. For example catagory for above is changed from 'ABC' to 'XYZ' of @design,

@design.changes   # => {} 
@design.catagory = 'XYZ'
@design.changes # => { 'catagory' => ['ABC', 'XYZ'] }

For references change in ROR



回答5:

For rails 5.1+ callbacks

To improve this post and complete other answers

The first answer will be deprecated for callbacks at next rails versions (after 5.1).
So the new way to check if an attribute has changed is with saved_change_to_attribute?

You can do:

@user.saved_change_to_street1? # => true/false

You can see more examples here at this post blog