确定在 _ save 回调之后 Rails 中修改了哪些属性?

我在我的模型观察者中设置了 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

有没有人对处理这个问题的最佳方法有什么建议,最好是使用模型观察者回调(这样就不会污染我的控制器代码) ?

141016 次浏览

您只需添加一个访问器,该访问器定义您更改的内容

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

Rails5.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)

Rails 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 3.1和 CouchRest: : Model (基于活动模型)。对于 after_update钩子中更改的属性,_changed?方法不返回 true,只在 before_update钩子中返回 true。我可以让它工作使用(新?)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

如果你可以在 before_save而不是 after_save上做到这一点,你可以使用以下方法:

self.changed

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

你亦可使用:

self.changes

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

对于那些想知道 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

对于任何后来看到这一点的人,因为它目前(2017年8月)高于谷歌: 值得一提的是,这种行为将在 Rails 5.2中改变,并且在 Rails 5.1中有弃用警告,因为 ActiveModel: : Dirty有所改变。

我要改变什么?

如果在 after_*回调函数中使用 attribute_changed?方法,您将看到类似下面的警告:

反对警告: 在 Rails 的下一个版本中,after 回调内部的 attribute_changed?行为将发生变化。新的返回值将反映 save返回后调用方法的行为(例如,与它现在返回的内容相反)。若要维护当前行为,请改为使用 saved_change_to_attribute?。(从/PATH _ TO/app/model/user.rb: 15的一些 _ callback 调用)

正如它所提到的,您可以通过用 saved_change_to_attribute?替换函数来轻松地解决这个问题。

同样地,如果您使用 attribute_change来获取 before-after 值,这也会发生变化并抛出以下内容:

反对警告: 在 Rails 的下一个版本中,after 回调内部的 attribute_change行为将发生变化。新的返回值将反映 save返回后调用方法的行为(例如,与它现在返回的内容相反)。若要维护当前行为,请改为使用 saved_change_to_attribute。(从/PATH _ TO/app/model/user.rb: 20的一些 _ callback 调用)

同样,如前所述,该方法将名称更改为返回 ["old", "new"]saved_change_to_attribute。 或者使用返回所有更改的 saved_changes,这些更改可以作为 saved_changes['attribute']访问

您可以向 after_update添加一个条件,如下所示:

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


...
end

不需要在 send_notification方法本身中添加条件。