Rails introduces ActiveRecord::Persistence#update_attribute!


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
end

Now 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
=> true

As 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"
=> false

Now 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.

Join Our Newsletter