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


85

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

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

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

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

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

    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

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

Answers:


173

您应该查看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的欢乐!


6
保存了他所要求的模型后,这将不起作用。
汤姆·罗西

4
@TomRossi,这些dirty调用适用于after_save(在Rails 2.3和3.x中)。我已经使用过几次了。
Harish Shetty 2012年

11
@TomRossi,脏标志在提交后被重置,因此它们after_commit将在Rails 3.x中引入的回调中不可用。他们肯定会工作的after_save
Harish Shetty 2012年

我不知道!我以为一旦保存便将其重置!
汤姆·罗西

5
@TomRossi几年前,我以相同的假设开始。当我尝试在after_save中检查脏标志时,它起作用了。本质上,after_saveafter DML和之间的状态的回调before_commit。您可以after_save通过引发异常来终止整个事务。如果您想在保存后做点什么而又不影响当前操作,请使用after_commit:-)
Harish Shetty 2012年

38

你可以用这个

self.changed

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

你也可以使用

self.changes

它以数组的形式返回更改后的列以及结果前后的哈希值


7
只需一点点说明,就不必self.在它们上使用了-您只需说changed和即可changes
user664833 2014年

@ user664833更具体地说,您可以省略self模型本身中的时间,但可以使用object.changed和在任何对象上调用它们object.changes。:)
约书亚·品特

4

我建议您看一下可用的状态机插件之一:

任一种都可以让您设置状态以及状态之间的转换。处理需求的非常有用和简便的方法。


我正在尝试rubyist-aasm。可以说我有类Claim <ActiveRecord :: Base include AASM aasm_column:status aasm_initial_state:pending aasm_state:pending,:enter =>:enter_pending def enter_pending Notifier.deliver_pending_notification(self)end end而且我数据库中的状态字段具有默认值“待定”。如果我在不填写状态字段的情况下进行Claim.create(以便它将运行“待定”),AASM是否将运行“ enter_pending”方法?
David C,2009年

2

对于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?。该方法在回调后确定保存调用是否更改了某个属性时很有用。

选件

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

0

我已经在很多地方看到了这个问题,所以我为此编写了一个很小的rubygem,以使代码更好看(并避免到处出现一百万个if / else语句):https : //github.com/ronna-s / on_change。希望对您有所帮助。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.