【问题标题】:How to detect attribute changes from model?如何检测模型的属性变化?
【发布时间】:2009-10-19 01:10:07
【问题描述】:

我想在 Rails 中创建一个在模型保存后执行的回调函数。

我有这个模型,声明有一个属性“状态”,该属性会根据声明的状态而变化,可能的值是待处理、认可、批准、拒绝

数据库有“状态”,默认值为“待定”。

我想在第一次创建模型或从一个状态更新到另一个状态后执行某些任务,具体取决于它从哪个状态更改。

我的想法是在模型中有一个函数:

    after_save :check_state

    def check_state
      # if status changed from nil to pending (created)
      do this

      # if status changed from pending to approved
      performthistask
     end

我的问题是如何在模型内更改之前检查以前的值?

【问题讨论】:

    标签: ruby-on-rails


    【解决方案1】:

    你应该看看ActiveModel::Dirty 模块: 您应该能够在您的 Claim 模型上执行以下操作:

    claim.status_changed?  # returns true if 'status' attribute has changed
    claim.status_was       # returns the previous value of 'status' attribute
    claim.status_change    # => ['old value', 'new value'] returns the old and 
                           # new value for 'status' attribute
    
    claim.name = 'Bob'
    claim.changed # => ["name"]
    claim.changes # => {"name" => ["Bill", "Bob"]}
    

    哦! Rails 的乐趣!

    【讨论】:

    • 这在他要求的模型保存后将不起作用。
    • @TomRossi,dirty 调用适用于 after_save(在 Rails 2.3 和 3.x 中)。我用过好几次了。
    • @TomRossi,脏标志在提交后被重置,因此它们在 Rails 3.x 中引入的after_commit 回调中不可用。他们肯定会在after_save 工作。
    • 我不知道!我以为它们在保存后会被重置!
    • @TomRossi 几年前我从同样的假设开始。当我尝试检查 after_save 中的脏标志时,它起作用了。本质上,after_saveafter DMLbefore_commit 之间状态的回调。您可以通过抛出异常来终止after_save 中的整个事务。如果您想在保存后做某事而不影响当前操作,请使用after_commit :-)
    【解决方案2】:

    你可以用这个

    self.changed
    

    它返回该记录中所有更改的列的数组

    你也可以使用

    self.changes
    

    它以数组的形式返回发生变化的列的哈希以及结果之前和之后的哈希

    【讨论】:

    • 只是一个小提示,说没有必要在这些上使用self.——你可以说changedchanges
    • @user664833 更具体地说,您可以在模型本身中省略self,但您可以使用object.changedobject.changes 在任何对象上调用它们。 :)
    【解决方案3】:

    对于 Rails 5.1+,您应该使用活动记录属性方法:saved_change_to_attribute?

    saved_change_to_attribute?(attr_name, **options)`

    我们上次保存时这个属性有变化吗?这种方法可以 调用为 saved_change_to_name? 而不是 saved_change_to_attribute?("name")。行为类似于 attribute_changed?。这个方法在回调之后很有用 判断 save 调用是否改变了某个属性。

    选项

    from传递时,这个方法会返回false,除非原来的 值等于给定的选项

    to 传递时,此方法将返回 false,除非该值是 更改为给定值

    所以如果你想根据属性值的变化调用一些方法,你的模型会是这样的:

    class Claim < ApplicationRecord
      
      after_save :do_this, if: Proc.new { saved_change_to_status?(from: nil, to: 'pending') }
    
      after_save :do_that, if: Proc.new { saved_change_to_status?(from: 'pending', to: 'approved') }
    
      
      def do_this
        ..
        ..
      end
    
      def do_that
        ..
        ..
      end
    
    end
    

    如果您不想检查回调中的值变化,您可以执行以下操作::

    class Claim < ApplicationRecord
    
      after_save: :do_this, if: saved_change_to_status?
    
    
      def do_this
        ..
        ..
      end
    
    end
    

    【讨论】:

      【解决方案4】:

      我建议您查看一个可用的状态机插件:

      任何一个都可以让您设置状态和状态之间的转换。处理您的要求的非常有用且简单的方法。

      【讨论】:

      • 我正在尝试 ruby​​ist-aasm。假设我有类 Claim :enter_pending def enter_pending Notifier.deliver_pending_notification(self) end end 我数据库中的状态字段具有默认值的“待定”。如果我在不填写状态字段的情况下执行 Claim.create(这样它就会运行“待处理”),AASM 会运行“enter_pending”方法吗?
      【解决方案5】:

      我在很多地方都看到了这个问题,所以我为它写了一个小 ruby​​gem,以使代码更好一点(并避免到处都有一百万个 if/else 语句):https://github.com/ronna-s/on_change。 我希望这会有所帮助。

      【讨论】:

        【解决方案6】:

        使用经过良好测试的解决方案(例如 state_machine gem)会更好。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-05-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多