Qiita Conference 2025

tenntenn (@tenntenn)

好奇心を原動力に行動するソフトウェアエンジニアになるために

2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rails の update_attribute はその時点で dirty な値を全てDBに保存する

Last updated at Posted at 2020-03-02

update_attribute で一つの属性値を変更しようとしたら、他の値も更新されてしまった

以下のような操作を行うと、 update_attribute は明示的に更新しようとしてない物も更新する。

# attr_1 と attr_2 という属性を持つ以下のような SomeModel がある時、
some_model = SomeModel.find(42)
some_model.attr_1 # => nil
some_model.attr_2 # => nil

# attr_1 を dirty にする
some_modal.attr_1 = 'this is ditry column'

# とりあえず attr_2 だけ保存しようかな…
some_model.update_attribute(:attr_2, 'this is updated')
some_model.attr_2_in_database # => 'this is updated'

# !?
some_model.attr_1_in_database # => 'this is ditry column'

Dirty な値が保存されるのは update_attribute の仕様です

ActiveRecord::Persisitence#update_attributeより引用(強調は引用者による):

Updates a single attribute and saves the record. This is especially useful for boolean flags on existing records. Also note that

  • Validation is skipped.
  • Callbacks are invoked.
  • updated_at/updated_on column is updated if that column is available.
  • Updates all the attributes that are dirty in this object.

翻訳(適当):

単一の属性値を更新し、レコードに保存します。このメソッドは特にすでに存在しているレコードのbooleanのフラグ(の更新)にとって便利です。注意点は次の通り

  • バリデーションはスキップされます。
  • コールバックは実行されます。
  • update_at/update_on カラムがあれば更新されます。
  • そのオブジェクトでdirtyな属性値は全て更新されます。

実装を見てみる

なんか変な挙動だな…とおもったので実装を見てみる。

activerecord/lib/active_record/persistence.rb より引用

activerecord/lib/active_record/persistence.rb
module ActiveRecord
  module Persistence
    # ...(中略)
    def update_attribute(name, value)
      name = name.to_s
      verify_readonly_attribute(name)
      public_send("#{name}=", value)

      save(validate: false)
    end
    # ...(後略)
  end
end

値をセットして save してるだけだった。 dirty な値が全部保存されてしまうのも納得の挙動だ。update_attribute は「単一の属性値をセットしたあとに save する」というショートハンドであって、「単一の属性値を保存する」メソッドではないと認識しておこう

付録(update_attributeの歴史)

メソッド名から想像される動作と実際の動作の違いが Rails のコードを全部読んだ人向けっぽいと思った。ライブラリとして小さかった昔からの名残ではないかと思い起源を調べてみた。するとやはり GitHub の rails/rails の最初のコミット(2004-12-24) から存在しており、動作もほぼ今と同じだった。

https://github.com/rails/rails/blob/db045dbbf6/activerecord/lib/active_record/base.rb#L709-L713 より引用(略は引用者による)

activerecord/lib/active_record/base.rb
module ActiveRecord
  class Base
    public
      # ...(中略)
      # Updates a single attribute and saves the record. This is especially useful for boolean flags on existing records.
      def update_attribute(name, value)
        self[name] = value
        save
      end
      # ...(後略)
  end
end

今のドキュメントと見比べると、メソッドのコメントは今と全く同じで、注意書きが追加されている。同じような勘違いをする人が多いので注意書きが追加されてったのだろうか。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up

Qiita Conference 2025 will be held!: 4/23(wed) - 4/25(Fri)

Qiita Conference is the largest tech conference in Qiita!

Keynote Speaker

ymrl、Masanobu Naruse, Takeshi Kano, Junichi Ito, uhyo, Hiroshi Tokumaru, MinoDriven, Minorun, Hiroyuki Sakuraba, tenntenn, drken, konifar

View event details
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?