如何从模型中检测属性更改?

Dav*_*d C 84 ruby-on-rails

我想在rails中创建一个回调函数,该函数在保存模型后执行.

我有这个模型,声明具有属性'status',该属性根据声明的状态而变化,可能的值是待定,认可,批准,拒绝

数据库具有"状态",默认值为"pending".

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

我的想法是在模型中有一个功能:

    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
Run Code Online (Sandbox Code Playgroud)

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

Har*_*tty 170

您应该查看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"]}
Run Code Online (Sandbox Code Playgroud)

哦! Rails的乐趣!

  • @TomRossi,脏标志在提交后重置,因此它们不会在Rails 3.x中引入的`after_commit`回调中可用.他们肯定会在`after_save`中工作. (10认同)
  • 保存模型后,这将不起作用,这是他要求的. (5认同)
  • @TomRossi几年前我就是以同样的假设开始的.当我试图检查after_save中的脏标志时它起作用了.本质上,`after_save`是`在DML之后'和`before_commit`之间的状态的回调.您可以通过抛出异常来终止`after_save`中的整个事务.如果你想在保存后做一些不影响当前操作的事情,请使用`after_commit` :-) (5认同)
  • @TomRossi,`dirty`调用在`after_save`中工作(在Rails 2.3和3.x中).我已经好几次使用它了. (3认同)

zea*_*uss 37

你可以用它

self.changed
Run Code Online (Sandbox Code Playgroud)

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

你也可以用

self.changes
Run Code Online (Sandbox Code Playgroud)

它返回一个已更改的列的散列,以及作为数组的结果之前和之后

  • 只是一个小小的注释,说没有必要在这些上使用`self. - 你可以说'改变'和'改变'. (7认同)

Raj*_*hra 7

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

已保存更改至属性?(attr_name, **选项)`

当我们上次保存时这个属性是否改变了?可以调用此方法来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
Run Code Online (Sandbox Code Playgroud)

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

class Claim < ApplicationRecord

  after_save: :do_this, if: saved_change_to_status?


  def do_this
    ..
    ..
  end

end
Run Code Online (Sandbox Code Playgroud)