Rails: Update Model Attribute Without Invoking Callbacks

Rails: Update model attribute without invoking callbacks

Rails 3.1 introduced update_column, which is the same as update_attribute, but without triggering validations or callbacks:

http://apidock.com/rails/ActiveRecord/Persistence/update_column

update_attibute/update_attributes skip callbacks

ActiveRecord::Persistence#update_column(name, value) and its companion ActiveRecord::Persistence#update_columns(attributes) are the methods to use if you want to skip callbacks and validations. From the class scope there is ActiveRecord::Relation#update_all(updates).

Alternatively you could go with JSON (instead of serializing the array) like:

class Bar < ActiveRecord::Base
def foo= array
write_attribute :foo, array.to_json
end

def foo
JSON.parse(read_attribute :foo)
end
end

and somewhere else:

Bar.first.update_column :foo, [1,2,3].to_json

At least, when you deal with serializing ActiveRecord::Store could be interesting for you.

How to update_attributes without executing before_save?

In rails 3.1 you will use update_column.

Otherwise:

In general way, the most elegant way to bypass callbacks is the following:

class Message < ActiveRecord::Base
cattr_accessor :skip_callbacks
before_save lambda { foo(publisher); bar }, :unless => :skip_callbacks # let's say you do not want this callback to be triggered when you perform batch operations
end

Then, you can do:

Message.skip_callbacks = true # for multiple records
my_message.update_attributes(:created_at => ...)
Message.skip_callbacks = false # reset

Or, just for one record:

my_message.update_attributes(:created_at => ..., :skip_callbacks => true)

If you need it specifically for a Time attribute, then touch will do the trick as mentioned by @lucapette .

Run a callback only if an attribute has changed in Rails

Rails 5.1+

class Page < ActiveRecord::Base
before_save :do_something, if: :will_save_change_to_status_id?

private

def do_something
# ...
end
end

The commit that changed ActiveRecord::Dirty is here: https://github.com/rails/rails/commit/16ae3db5a5c6a08383b974ae6c96faac5b4a3c81

Here is a blog post on these changes: https://www.fastruby.io/blog/rails/upgrades/active-record-5-1-api-changes

Here is the summary I made for myself on the changes to ActiveRecord::Dirty in Rails 5.1+:

ActiveRecord::Dirty

https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Dirty.html

Before Saving (OPTIONAL CHANGE)

After modifying an object and before saving to the database, or within the before_save filter:

  • changes should now be changes_to_save
  • changed? should now be has_changes_to_save?
  • changed should now be changed_attribute_names_to_save
  • <attribute>_change should now be <attribute>_change_to_be_saved
  • <attribute>_changed? should now be will_save_change_to_<attribute>?
  • <attribute>_was should now be <attribute>_in_database

After Saving (BREAKING CHANGE)

After modifying an object and after saving to the database, or within the after_save filter:

  • saved_changes (replaces previous_changes)
  • saved_changes?
  • saved_change_to_<attribute>
  • saved_change_to_<attribute>?
  • <attribute>_before_last_save


Rails <= 5.0

class Page < ActiveRecord::Base
before_save :do_something, if: :status_id_changed?

private

def do_something
# ...
end
end

This utilizes the fact that the before_save callback can conditionally execute based on the return value of a method call. The status_id_changed? method comes from ActiveModel::Dirty, which allows us to check if a specific attribute has changed by simply appending _changed? to the attribute name.

When the do_something method should be called is up to your needs. It could be before_save or after_save or any of the defined ActiveRecord::Callbacks.

Ruby on Rails skip update callback on specific method in model

You can use update_columns to update colums without triggering callbacks or validations.

So in your case

if self.status_was == active and self.status == paused
self.update_columns(:missing_schedule_at => nil)
self.schedules.destroy_all
end

http://apidock.com/rails/ActiveRecord/Persistence/update_columns

Rails update_attributes without save?

I believe what you are looking for is assign_attributes.

It's basically the same as update_attributes but it doesn't save the record:

class User < ActiveRecord::Base
attr_accessible :name
attr_accessible :name, :is_admin, :as => :admin
end

user = User.new
user.assign_attributes({ :name => 'Josh', :is_admin => true }) # Raises an ActiveModel::MassAssignmentSecurity::Error
user.assign_attributes({ :name => 'Bob'})
user.name # => "Bob"
user.is_admin? # => false
user.new_record? # => true

Rails. Update model attributes on save

Change your code to following

before_save :calculate_and_save_budget_contingency

def calculate_and_save_budget_contingency
self.total_contingency = self.budget_contingency + self.risk_contingency
end

Reason for that is - if you run save in after_save you end up in infinite loop: a save calls after_save callback, which calls save which calls after_save, which...

In general it's wise you use after save only for changing associated models, etc.



Related Topics



Leave a reply



Submit