Rails has multiple methods to update a record,
each one reserved for a unique purpose.
While the traditional update is used to update records by executing validations,
callbacks
and
track changes, there might be a few instances where one would like to skip validations.
Using update_attribute does just that.
Before
To skip validations on the update,
use update_attribute that ensures:
- Validations are skipped
- Callbacks are invoked
- updated_at / updated_on column is updated (if that column is available)
- Tracks changes via ActiveModel::Dirty
Let’s look at an example!
class Blog < ApplicationRecord
validates_presence_of :text
before_save :check_if_title_contains_special_characters
def check_if_title_contains_special_characters
throw(:abort) if title.index( /[^[:alnum:]]/ ).present?
end
endNow let’s compare the differences between update, update! and update_attribute,
> blog = Blog.last
=> #<Blog id: 1, title: "Ruby on Rails Tutorial: Learn Web Development with...", text: nil ... >
> blog.update text: nil
=> false
> blog.update! text: nil
Traceback (most recent call last):
1: from (irb):4
ActiveRecord::RecordInvalid (Validation failed: Text can't be blank)
> blog.update_attribute :text, nil
=> trueAs we can see, validations are skipped when using update_attribute
but what is interesting is the behavior of update and update!.
While update silently fails,
calling update! throws an ActiveRecord::RecordInvalid error.
Let’s compare callbacks now.
> blog.update title: "Hel@lo"
=> false
> blog.update! title: "Hel@lo"
Traceback (most recent call last):
1: from (irb):10
ActiveRecord::RecordNotSaved (Failed to save the record)
> blog.update_attribute :title, "Hel@lo"
=> falseNow update and update_attribute silently fail,
while update! gives us a better understanding.
Unfortunately, there is no equivalent of update! for update_attribute that can
give us insights into record updates.
After
Fortunately, Rails introduces update_attribute! which can be used to prevent “silent” updates.
> blog.update title: "Hel@lo"
=> false
> blog.update! title: "Hel@lo"
Traceback (most recent call last):
1: from (irb):10
ActiveRecord::RecordNotSaved (Failed to save the record)
> blog.update_attribute :title, "Hel@lo"
=> false
> blog.update_attribute! :title, "Hel@lo"
Traceback (most recent call last):
1: from (irb):12
ActiveRecord::RecordNotSaved (Failed to save the record)This change adds a new method,
ActiveRecord::Persistence#update_attribute! which calls save! instead of save.
The update_attribute! method will now raise an ActiveRecord::RecordNotSaved error should any before_* callbacks throw :abort.
