🛤️

ActiveRecord の change 系メソッドまとめ

2023/04/04に公開

「作成」→「更新」→「空更新」とした場合の変化。

作成 create(foo: "a")

method before_save after_save
will_save_change_to_attribute?(:foo) true false
attribute_change_to_be_saved(:foo) [nil, "a"] nil
attribute_in_database(:foo) nil "a"
changes_to_save {"foo"=>[nil, "a"]} {}
has_changes_to_save? true false
changed_attribute_names_to_save ["foo"] []
attributes_in_database {"foo"=>nil} {}
saved_change_to_attribute?(:foo) false true
saved_change_to_attribute(:foo) nil [nil, "a"]
attribute_before_last_save(:foo) nil nil
saved_changes {} {"id"=>[nil, 1], "foo"=>[nil, "a"]}
saved_changes? false true

更新 update(foo: "b")

method before_save after_save
will_save_change_to_attribute?(:foo) true false
attribute_change_to_be_saved(:foo) ["a", "b"] nil
attribute_in_database(:foo) "a" "b"
changes_to_save {"foo"=>["a", "b"]} {}
has_changes_to_save? true false
changed_attribute_names_to_save ["foo"] []
attributes_in_database {"foo"=>"a"} {}
saved_change_to_attribute?(:foo) true true
saved_change_to_attribute(:foo) nil ["a", "b"]
attribute_before_last_save(:foo) nil "a"
saved_changes {} {"foo"=>["a", "b"]}
saved_changes? false true

空更新 update({})

method before_save after_save
will_save_change_to_attribute?(:foo) false false
attribute_change_to_be_saved(:foo) nil nil
attribute_in_database(:foo) "b" "b"
changes_to_save {} {}
has_changes_to_save? false false
changed_attribute_names_to_save [] []
attributes_in_database {} {}
saved_change_to_attribute?(:foo) false false
saved_change_to_attribute(:foo) nil nil
attribute_before_last_save(:foo) nil "b"
saved_changes {} {}
saved_changes? false false

検証用のコード

require "active_record"
ActiveRecord::VERSION::STRING             # => "7.1.3.2"
ActiveSupport::LogSubscriber.colorize_logging = false
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Migration.verbose = false
ActiveRecord::Schema.define do
  create_table :users do |t|
    t.string :foo
  end
end

class User < ActiveRecord::Base
  before_save do
    will_save_change_to_attribute?(:foo)  # => true, true, false
    attribute_change_to_be_saved(:foo)    # => [nil, "a"], ["a", "b"], nil
    attribute_in_database(:foo)           # => nil, "a", "b"
    changes_to_save                       # => {"foo"=>[nil, "a"]}, {"foo"=>["a", "b"]}, {}
    has_changes_to_save?                  # => true, true, false
    changed_attribute_names_to_save       # => ["foo"], ["foo"], []
    attributes_in_database                # => {"foo"=>nil}, {"foo"=>"a"}, {}
    saved_change_to_attribute?(:foo)      # => false, false, false
    saved_change_to_attribute(:foo)       # => nil, nil, nil
    attribute_before_last_save(:foo)      # => nil, nil, nil
    saved_changes                         # => {}, {}, {}
    saved_changes?                        # => false, false, false
  end

  after_save do
    will_save_change_to_attribute?(:foo)  # => false, false, false
    attribute_change_to_be_saved(:foo)    # => nil, nil, nil
    attribute_in_database(:foo)           # => "a", "b", "b"
    changes_to_save                       # => {}, {}, {}
    has_changes_to_save?                  # => false, false, false
    changed_attribute_names_to_save       # => [], [], []
    attributes_in_database                # => {}, {}, {}
    saved_change_to_attribute?(:foo)      # => true, true, false
    saved_change_to_attribute(:foo)       # => [nil, "a"], ["a", "b"], nil
    attribute_before_last_save(:foo)      # => nil, "a", "b"
    saved_changes                         # => {"id"=>[nil, 1], "foo"=>[nil, "a"]}, {"foo"=>["a", "b"]}, {}
    saved_changes?                        # => true, true, false
  end
end

user = User.create!(foo: "a")             # => #<User id: 1, foo: "a">
user.reload                               # => #<User id: 1, foo: "a">
user.update!(foo: "b")                    # => true
user.reload                               # => #<User id: 1, foo: "b">
user.update!({})                          # => true

Discussion