如何从模型中检测属性变化?

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

我有这个模型,索赔,有一个属性“状态”的变化取决于状态的索赔,可能的价值是未决,背书,批准,拒绝

数据库具有“ state”,默认值为“挂起”。

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

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

    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

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

76391 次浏览

I recommend you have a look at one of the available state machine plugins:

Either one will let you setup states and transitions between states. Very useful and easy way of handling your requirements.

You should look at ActiveModel::Dirty module: You should be able to perform following actions on your Claim model:

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"]}

Oh! the joys of Rails!

you can use this

self.changed

it return an array of all columns that changed in this record

you can also use

self.changes

which returns a hash of columns that changed and before and after results as arrays

I've seen the question rise in many places, so I wrote a tiny rubygem for it, to make the code a little nicer (and avoid a million if/else statements everywhere): https://github.com/ronna-s/on_change. I hope that helps.

You will be much better off using a well tested solution such as the state_machine gem.

For Rails 5.1+, you should use active record attribute method: saved_change_to_attribute?

saved_change_to_attribute?(attr_name, **options)`

Did this attribute change when we last saved? This method can be invoked as saved_change_to_name? instead of saved_change_to_attribute?("name"). Behaves similarly to attribute_changed?. This method is useful in after callbacks to determine if the call to save changed a certain attribute.

Options

from When passed, this method will return false unless the original value is equal to the given option

to When passed, this method will return false unless the value was changed to the given value

So your model will look like this, if you want to call some method based on the change in attribute value:

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

And if you don't want to check for value change in callback, you can do the following::

class Claim < ApplicationRecord


after_save: :do_this, if: saved_change_to_status?




def do_this
..
..
end


end