【问题标题】:Rails 3 check if attribute changedRails 3检查属性是否更改
【发布时间】:2011-06-30 09:13:16
【问题描述】:

需要在 Rails 3 中更新之前检查属性块是否已更改。

街道 1、街道 2、城市、州、邮政编码

我知道我可以使用类似的东西

if @user.street1 != params[:user][:street1]
  then do something....
end

但是那段代码真的很长。有没有更清洁的方法?

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3


    【解决方案1】:

    查看ActiveModel::Dirty(默认情况下适用于所有型号)。该文档非常好,但它可以让您执行以下操作:

    @user.street1_changed? # => true/false
    

    【讨论】:

    • 如果您试图查看表单中的属性是否已更改(这似乎是 OP 正在做的事情),这将无济于事。
    • 当然可以。如果您使用 params 哈希分配值,则这些值来自表单。
    • 如果我想做这样的事情怎么办:model.collection.changed_size_by?或类似的东西
    • @MauroDias 你必须编写自己的逻辑来跟踪这样的集合属性。
    • 我将尝试为此使用一些测试库。无论如何,可能是一种解决方法 TY
    【解决方案2】:

    这就是我解决检查多个属性变化的问题的方法。

    attrs = ["street1", "street2", "city", "state", "zipcode"]
    
    if (@user.changed & attrs).any?
      then do something....
    end
    

    changed 方法返回为该对象更改的属性数组。

    @user.changedattrs 都是数组,所以我可以得到交集(参见 ary & other ary 方法)。交集的结果是一个数组。通过在数组上调用any?,如果至少有一个交叉点,我会得到正确的结果。

    同样非常有用的是,changed_attributes 方法返回属性的哈希值及其原始值,changes 返回属性的哈希值及其原始值和新值(在数组中)。

    您可以查看 APIDock 以了解哪些版本支持这些方法。

    http://apidock.com/rails/ActiveModel/Dirty

    【讨论】:

    • 这是一个很好的方法。从历史上看,我已经完成了if attrs.any?{|attr| @user.send("#{attr}_changed?")} 当我想检查几个不同属性中的一个是否发生了变化时——当然,我只对我自己定义的 attrs 执行此操作,因为我不喜欢将用户参数放入send 方法。 ;)
    • previously_changed适配,一般在after_commit:if (@user.previous_changes.keys & attrs).any?
    【解决方案3】:

    ActiveModel::Dirty 对我不起作用,因为 @model.update_attributes() 隐藏了更改。所以这就是我在控制器的 update 方法中检测到的变化:

    def update
      @model = Model.find(params[:id])
      detect_changes
    
      if @model.update_attributes(params[:model])
        do_stuff if attr_changed?
      end
    end
    
    private
    
    def detect_changes
      @changed = []
      @changed << :attr if @model.attr != params[:model][:attr]
    end
    
    def attr_changed?
      @changed.include :attr
    end
    

    如果您尝试检测大量属性更改,但它可能会变得混乱。可能不应该在控制器中这样做,但是嗯。

    【讨论】:

    • 对于它的价值,您可以使用previous_changes 执行此操作,默认情况下也可用。
    • #update_attributes 不会隐藏更改。它保存记录,因此模型已更新并且没有更改。您需要更改模型中的字段并在保存之前检查changed?。 IE。 @model.field = 'foo' 或 @model.attributes = @model.attributes.merge(params[:model])
    【解决方案4】:

    以上答案更好,但对于知识,我们还有另一种方法, 让对象的“类别”列值更改(@design),

    @design.changes.has_key?('catagory')
    

    .changes 将返回一个散列,其中键作为列名,值作为数组,每列有两个值 [old_value, new_value]。例如上面的类别从@design的'ABC'更改为'XYZ',

    @design.changes   # => {} 
    @design.catagory = 'XYZ'
    @design.changes # => { 'catagory' => ['ABC', 'XYZ'] }
    

    参考change in ROR

    【讨论】:

      【解决方案5】:

      对于 rails 5.1+ 回调

      从 Ruby on Rails 5.1 开始,attribute_changed?attribute_was ActiveRecord 方法将被弃用

      使用saved_change_to_attribute? 代替attribute_changed?

      @user.saved_change_to_street1? # => true/false
      

      更多例子here

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-06-14
        • 1970-01-01
        • 1970-01-01
        • 2015-10-17
        • 2014-02-17
        • 1970-01-01
        • 2011-03-04
        • 1970-01-01
        相关资源
        最近更新 更多