确定在Rails after_save回调中更改了哪些属性?


153

我正在模型观察器中设置after_save回调,以仅在模型的发布属性从false更改为true时发送通知。既然方法改变了?仅在保存模型之前有用,我目前(但未成功)尝试这样做的方式如下:

def before_save(blog)
  @og_published = blog.published?
end

def after_save(blog)
  if @og_published == false and blog.published? == true
    Notification.send(...)
  end
end

有没有人对处理此问题的最佳方法有任何建议,最好使用模型观察者回调(以免污染我的控制器代码)?

Answers:


182

Rails 5.1+

用途saved_change_to_published?

class SomeModel < ActiveRecord::Base
  after_update :send_notification_after_change

  def send_notification_after_change
    Notification.send(…) if (saved_change_to_published? && self.published == true)
  end

end

或者,如果您愿意,saved_change_to_attribute?(:published)

轨道3–5.1

警告

这种方法在Rails 5.1中有效(但在5.1中已弃用,并且在5.2中有重大更改)。您可以在请求请求中阅读有关更改的信息。

after_update模型的过滤器中,您可以使用_changed?访问器。因此,例如:

class SomeModel < ActiveRecord::Base
  after_update :send_notification_after_change

  def send_notification_after_change
    Notification.send(...) if (self.published_changed? && self.published == true)
  end

end

它只是工作。


忘了我上面说的-在Rails 2.0.5中不起作用。因此,一个有益的补充的Rails 3
stephenr

4
我认为after_update现在已弃用?无论如何,我在一个after_save钩子中尝试了这一点,这似乎很好。(显然,after_save中的changes()哈希值尚未被重置。)
Tyler Rick

3
我必须将此行包含在model.rb文件中。包括ActiveModel :: Dirty
coderVishal

13
在更高版本的Rails中,您可以将条件添加到after_update呼叫中:after_update :send_notification_after_change, if: -> { published_changed? }
Koen。

11
Rails 5.2中将对API进行一些更改。您必须做saved_change_to_published?saved_change_to_published在回调过程中获取更改
alopez02 '18

183

对于那些想知道刚刚在after_save回调中所做的更改的人:

Rails 5.1及更高版本

model.saved_changes

导轨<5.1

model.previous_changes

另请参阅:http : //api.rubyonrails.org/classes/ActiveModel/Dirty.html#method-i-previous_changes


2
当您不想使用模型回调并且需要进行有效的保存才能执行进一步的功能时,此方法非常有用。
戴夫·罗伯逊

太棒了!感谢您发布此信息。
jrhicks,2015年

太棒了!谢谢你
丹尼尔·洛根

4
只需澄清一下:在我的测试中(第4步),如果您使用的是after_save回调,self.changed?则is trueself.attribute_name_changed?is true,但self.previous_changes返回一个空哈希。
sandre89 '16

9
在Rails 5.1及更高版本中不推荐使用此功能。使用saved_changesafter_save的回调,而不是
rico_mac

71

对于任何后来在以后看到它的人来说,目前(2017年8月)在google上排名最高:值得一提的是,此行为将在Rails 5.2中更改,并且从Rails 5.1开始具有弃用警告,因为ActiveModel :: Dirty有所更改。

我要改变什么?

如果attribute_changed?after_*-callbacks中使用method ,则会看到类似以下的警告:

声明警告:attribute_changed?在下一个版本的Rails中,after回调内部的行为将发生变化。新的返回值将反映在save返回后调用方法的行为(例如,与现在返回的相反)。要保持当前行为,请saved_change_to_attribute?改用。(从/PATH_TO/app/models/user.rb:15的some_callback调用)

如前所述,您可以通过将功能替换为来轻松解决此问题saved_change_to_attribute?。因此,例如,name_changed?变为saved_change_to_name?

同样,如果您使用attribute_change来获取前后值,则该值也会发生变化并引发以下情况:

声明警告:attribute_change在下一个版本的Rails中,after回调内部的行为将发生变化。新的返回值将反映在save返回后调用方法的行为(例如,与现在返回的相反)。要保持当前行为,请saved_change_to_attribute改用。(从/PATH_TO/app/models/user.rb:20的some_callback调用)

再次提及,该方法将名称更改为saved_change_to_attributereturn ["old", "new"]。或使用saved_changes,它返回所有更改,并且可以通过进行访问saved_changes['attribute']


2
请注意,此有用的响应还包括弃用attribute_was方法的变通方法:saved_change_to_attribute改为使用。
Joe Atzberger

47

如果您可以在before_save而不是上执行此操作,则after_save可以使用此功能:

self.changed

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

您还可以使用:

self.changes

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


8
除了这些在after_回调中不起作用外,这实际上就是问题所在。@jacek-głodek在下面的答案是正确的。
Jazz

更新了答案以明确说明这仅适用于before_save
mahemoff 2015年

1
这是哪个Rails版本?在Rails 4中,self.changed可以在after_save回调中使用。
sandre89 '16

很棒!还要注意的self.changed是,结果是一个字符串数组!(不是符号!)["attr_name", "other_attr_name"]
路加福音

8

“选择的”答案对我不起作用。我在CouchRest :: Model(基于Active Model)上使用rails 3.1。_changed?对于after_update钩子中已更改的属性,这些方法不会返回true ,而仅对钩子中的属性返回true before_update。我能够使用(new?)around_update钩子使其工作:

class SomeModel < ActiveRecord::Base
  around_update :send_notification_after_change

  def send_notification_after_change
    should_send_it = self.published_changed? && self.published == true

    yield

    Notification.send(...) if should_send_it
  end

end

1
选择的答案对我也不起作用,但确实如此。谢谢!我正在使用ActiveRecord 3.2.16。
本李

5

您可以after_update像这样添加条件:

class SomeModel < ActiveRecord::Base
  after_update :send_notification, if: :published_changed?

  ...
end

无需在send_notification方法本身内添加条件。


-17

您只需添加定义您更改内容的访问器

class Post < AR::Base
  attr_reader :what_changed

  before_filter :what_changed?

  def what_changed?
    @what_changed = changes || []
  end

  after_filter :action_on_changes

  def action_on_changes
    @what_changed.each do |change|
      p change
    end
  end
end

这是一个非常糟糕的做法,甚至不要考虑。
努诺席尔瓦
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.