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 bechanges_to_save
changed?
should now behas_changes_to_save?
changed
should now bechanged_attribute_names_to_save
<attribute>_change
should now be<attribute>_change_to_be_saved
<attribute>_changed?
should now bewill_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
(replacesprevious_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
In a Sinatra App on Heroku, Session Is Not Shared Across Dynos
What Is the Purpose of a 'Transient Do' Block in Factorybot Factories
Rvm: How to Use Gems from a Different Ruby
Multiple Level Nested Layout in Rails 3
Elasticsearch Terms Aggregation by Entire Field
Equivalent of Iconv.Conv("Utf-8//Ignore",...) in Ruby 1.9.X
Vim Ruby Mismatch on MAC High Sierra
Ruby, Tor and Net::Http::Proxy
Rails 4 Update Nested Attributes
Installing Nokogiri MAC Os X 10.8.2 Xcode Installed
Use Separate Authentication Model with Devise on Rails
How to Make the Url's in Ruby on Rails Seo Friendly Knowing a @Vendor.Name
Rails Custom Validation - Only One Record Can Be True
Why Doesn't Module.Method_Defined(:Method) Work Correctly
Sequel Accessing Many_To_Many Join Table When Adding Association