🐕

ActiveRecord で特定のレコードを更新できないようにする

2024/09/24に公開

ActiveRecord の特定のカラムで『これは最初に割り当てた以降は変更してほしくない!』みたいな扱いをしたいことが稀によくあります。
こういうのを機械的に更新できないようにする場合に attr_readonly が利用できます。

class User < ActiveRecord::Base
  # name と created_at を更新不可にする
  attr_readonly :name, :created_at
end

homu = User.create!(name: "homu", age: 14)

# 値を割り当てようとすると例外が発生する
# error: `_write_attribute': name (ActiveRecord::ReadonlyAttributeError)
homu.name = "mami"
p homu.name   # => "mami"

また update! に関しても同様に例外になります。

# update! に関しても同様
# error: `_write_attribute': name (ActiveRecord::ReadonlyAttributeError)
homu.update!(name: "mami")
p homu.reload.name   # => "homu"

また attr_readonly のカラムに対して値を割り当てたときに例外が発生するかどうかは config.active_record.raise_on_assign_to_attr_readonlyActiveRecord.raise_on_assign_to_attr_readonly で制御できます。

# false で例外が発生しなくなる
ActiveRecord.raise_on_assign_to_attr_readonly = false

class User < ActiveRecord::Base
  attr_readonly :name, :created_at
end

homu = User.create!(name: "homu", age: 14)

# ここで値を割り当ててもエラーにならないが実際には値は更新されていない
homu.name = "mami"
homu.save!
p homu.reload.name   # => "homu"

raise_on_assign_to_attr_readonly のデフォルト値は以下のように設定されています。

| Rails | 値 |
| 7.0 以前 | false |
| 7.1 以降 | true |

ちなみに #update_columns の場合は raise_on_assign_to_attr_readonly の設定によらずに例外が発生します。

ActiveRecord.raise_on_assign_to_attr_readonly = false

# error: `verify_readonly_attribute': name is marked as readonly (ActiveRecord::ActiveRecordError)
homu.update_columns(name: "mami")

参照

GitHubで編集を提案

Discussion