【问题标题】:How can I disallow updates except for on one field?除了一个字段,我如何禁止更新?
【发布时间】:2011-05-10 15:59:19
【问题描述】:

我一直在模型中使用它来阻止对某些模型的更新:

def update
  self.errors.add_to_base( "Cannot update a #{ self.to_s }" )
end

我现在正在编写一个为模型提供一些额外功能的插件,我需要更新模型中的一个字段。如果我不使用插件,我会直接在模型中执行此操作...

def update
  if self.changed == ['my_field']
    super
  else
    self.errors.add_to_base( "Cannot update a #{ self.to_s }" )       
  end
end

我不能从我的插件中做同样的事情,因为我不知道更新行为是 ActiveRecord 默认值,还是被覆盖以防止更新。是否有另一种方法可以防止记录更新,同时允许我覆盖特定字段(并且仅在我的插件应用于此模型的情况下)。

【问题讨论】:

    标签: ruby-on-rails ruby activerecord


    【解决方案1】:

    我只是使用 rails params.require 方法将您想要允许的属性列入白名单。

    def update
      if @model.update(update_model_params)
        render json: @model, status: :ok
      else
        render json: @model.errors, status: :unprocessable_entity
      end
    end
    
    private
      def update_prediction_params
        params.require(:model).permit(:editable_attribute)
      end
    

    【讨论】:

      【解决方案2】:

      在这里玩游戏晚了,但对于查看此问题的人,您可以使用 attr_readonly 允许在创建时写入字段,但不允许更新。

      http://api.rubyonrails.org/classes/ActiveRecord/ReadonlyAttributes/ClassMethods.html

      我认为它从 Rails 2.0 开始就可以使用了

      棘手的部分是,如果您有任何 attr_accessible 属性,您还必须在其中列出您的只读属性(或者您在创建时遇到批量分配错误):

      class Post < ActiveRecord::Base
        attr_readonly :original_title
        attr_accessible :latest_title, :original_title
      end
      

      【讨论】:

      【解决方案3】:

      首先,您应该为此类事情使用 before_update 回调,而不是覆盖更新。其次,您可以将可更新的属性存储在模型上,然后使用插件对其进行更新。我只是在浏览器中写的,所以可能是错误的。

        attr_accessor :updatable_attributes
        before_update :prevent_update
      
        private
        def prevent_update
          return true if self.changed == self.updatable_attributes
          self.errors.add_to_base "Cannot update a #{ self.to_s }"
          false
        end
      end
      

      【讨论】:

      • 谢谢。我意识到需要我的 prevent_update 逻辑的模型也是需要插件的模型,所以我已经将全部内容移到了插件中,使用了你建议的 before_update 过滤器。
      • 在rails 3.*中,通过self.errors.add :base, "Cannot update..."添加错误
      • 我认为您可能希望 if 子句改为 return true if self.changed|self.updatabale_attributes == self.updatabale_attributes,以便您还可以更新 updatable_attributes 的子集。
      • 从某个时候before_update 回调失去了阻止更新的功能,无论它返回什么,或者它添加了什么错误。改用验证,相关问题:link
      【解决方案4】:

      这是为了防止批量分配吗? attr_accessible / attr_protected 不会做你需要的吗?


      编辑,只是为了说明关于回调的一般观点。

       module MyModule
         def MyModule.included(base)
           base.send :alias_method_chain, :prevent_update, :exceptions
         end
      
         def prevent_update_with_exceptions
         end
       end
      
       class MyModel < ActiveRecord::Base
         before_validation :prevent_update
      
         def prevent_update
         end
      
         include MyModule
       end
      

      【讨论】:

      • 不,它在模型级别执行业务规则。对除一个以外的每个字段执行 attr_readonly 可以完成这项工作,但相当冗长。
      • before_validation 回调怎么样?您可以检查哪些属性已更改以及模型是否为新模型,然后阻止更新?
      • 不确定我是否完全理解您的建议......您是说在模型中添加一个 before_validation 回调,这将阻止更新。那么在我的插件中我应该怎么做才能允许更新一个字段?
      • 我也不完全确定我是否理解你实际上需要做什么,但插件可以添加一个回调,它使用 Rails 的部分更新来查看哪些字段已更改并酌情添加错误.
      • 嗯,错误添加功能是原始模型行为的一部分,因此默认情况下模型不应该是可更新的。这可以通过覆盖更新方法或使用您建议的回调来完成。问题是如何允许我的插件更新其中一个字段?使用像你这样的回调建议我使用 !self.new? 引发错误,但我的插件仍然会犯规 - 插件无法覆盖该行为。或者有吗?
      猜你喜欢
      • 2019-04-06
      • 1970-01-01
      • 1970-01-01
      • 2014-08-10
      • 1970-01-01
      • 2011-11-27
      • 2019-11-18
      • 2018-08-24
      • 1970-01-01
      相关资源
      最近更新 更多