Rails 7.1: touchやupdate_column(s)によるreadonlyモデル更新を修正(翻訳)
Railsのモデルには、非常にシンプルな方法でデータベースにアクセスできるActive Recordのメソッドがたくさんあります。モデルの作成、更新、削除はきわめて簡単です。
しかし、場合によってはモデルを"readonly"とマーキングすることで更新や削除を不可能にし、読み取りだけを許可したいことがあります。このような状況は、大規模なリファクタリングで起きることがあります。使わなくなったモデルがあったとしても、すぐ消してよいとは限らず、読み取りだけは行う必要があるかもしれません。
改修前
これを行うには、そのモデルにreadonly?
メソッドを追加してtrue
を返すようにします。
class Rocket
...
def readonly?
true
end
end
これで、モデルでupdate
やdestroy
を実行すると期待通りエラーが発生するようになります。
irb(main):001:0> rocket.update name: "A1"
TRANSACTION (5.4ms) BEGIN
User Load (6.2ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
TRANSACTION (1.8ms) ROLLBACK
Traceback (most recent call last):
/Users/swaathi/.rbenv/versions/2.7.5/lib/ruby/gems/2.7.0/bundler/gems/rails-08af60e01dcf/activerecord/lib/active_record/persistence.rb:1147:in `_raise_readonly_record_error': Rocket is marked as readonly (ActiveRecord::ReadOnlyRecord)
irb(main):002:0> rocket.destroy
Traceback (most recent call last):
/Users/swaathi/.rbenv/versions/2.7.5/lib/ruby/gems/2.7.0/bundler/gems/rails-08af60e01dcf/activerecord/lib/active_record/persistence.rb:1147:in `_raise_readonly_record_error': Rocket is marked as readonly (ActiveRecord::ReadOnlyRecord)
どちらの場合もActiveRecord::ReadOnlyRecord
エラーが発生します。
しかし、モデルでtouch
メソッドを実行してもエラーが発生しません。
irb(main):002:0> rocket.touch
TRANSACTION (0.5ms) BEGIN
Rocket Update (14.2ms) UPDATE `rockets` SET `rockets`.`updated_at` = '2022-08-14 06:40:24.291104' WHERE `rockets`.`id` = 1
TRANSACTION (5.6ms) COMMIT
=> true
readonlyモデルでtouch
メソッドを呼び出してもさほど被害はありませんが、update_columns
を呼び出してしまったときの被害は深刻です。残念ながらモデルをreadonlyにしても、モデルの更新をあらゆる方法で防げるわけではありません。readonlyモデルが不当に更新されてしまうと、データが破損してしまいます。
改修後
この問題が#44839で指摘されると、ただちにRailsチームが#44845でパッチを当てました。
この問題は、Actrive Recordのupdate_columns
やtouch
を更新する形で修正されました。
# activerecord/lib/active_record/persistence.rb
...
module ActiveRecord
module Persistence
extend ActiveSupport::Concern
module ClassMethods
def update_columns
...
_raise_readonly_record_error if readonly?
...
end
end
end
end
これで、update_columns
やtouch
を呼び出してもreadonlyモデルが更新されることはなくなりました。
概要
元サイトの許諾を得て翻訳・公開いたします。
日本語タイトルは内容に即したものにしました。
参考: 週刊Railsウォッチ20220829前編
touch
、update_column
、update_columns
がreadonlyレコードでエラーを出すよう修正この#44845はRails 7.0.3で反映済みです(Changelogには記載されていません)。