学习ActiveRecord方法touch和update_columns不再对只读模型起作用

121 阅读2分钟

Rails模型有几个ActiveRecord方法,使得与数据库的交互非常简单。 模型可以非常容易地被创建、更新和销毁。

然而,在某些情况下,人们可能想把一个模型标记为 "只读",意思是只允许读操作--不允许更新或删除。 这样的情况可能发生在一次大的重构过程中。 一个模型已经退役了,但我们可能还没有准备好把它清除掉。 我们可能仍然需要读取它。

之前

这可以通过给类添加一个readonly? 方法来实现,该方法返回true?

class Rocket
  ...
  
  def readonly?
    true
  end
end

现在,当我们试图更新或删除模型时,我们得到一个错误。

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 的错误。

然而,当我们试图触摸模型时,我们没有得到错误。

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

虽然touch 调用不会造成什么损害,但update_columns 调用却会。 不幸的是,将一个模型标记为只读并不能阻止我们以所有可能的方式更新该模型。 这使得数据被不正当的更新所破坏。

之后

人们很快就指出了这一差异,然而,Rails团队甚至更快地发布了一个补丁!

对ActiveRecord中的update_columnstouch 模型进行了简单的更新,解决了这个问题。

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_columnstouch 被调用时,只读模型不再被更新。