确定Rails after_save回调中哪些属性发生了变化?

mod*_*ron 138 model ruby-on-rails callback observer-pattern ruby-on-rails-4

我正在我的模型观察器中设置一个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
Run Code Online (Sandbox Code Playgroud)

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

Rad*_*sky 170

在你的after_update模型上的过滤器,你可以使用_changed?访问(至少在Rails 3中,不能确定为Rails 2).例如:

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

它只是有效.

  • 在Rails的更高版本中,您可以将条件添加到`after_update`调用:`after_update:send_notification_after_change,if: - > {published_changed?}` (13认同)
  • Rails 5.2中将对API进行一些更改。您必须执行`saved_change_to_published?`或`saved_change_to_published`才能在回调期间获取更改 (7认同)
  • 我想after_update现在已被弃用了吗?无论如何,我在after_save钩子中尝试了这个,这似乎工作正常.(显然,在after_save中,仍然没有重置changes()哈希.) (4认同)
  • attribute_changed?在Rails 4中为我工作 (4认同)
  • 好吧,效果很好 - 包括Rails 2.3.8. (2认同)
  • 我必须在model.rb文件中包含这一行._include ActiveModel :: Dirty_ (2认同)

Jac*_*dek 165

对于那些想要了解保存后更改的人,您应该使用

model.saved_changes
Run Code Online (Sandbox Code Playgroud)

这有点像after_save但它仍然有效after_save,等等.我发现这些信息很有用,所以也许你也会这样.

在Rails 5.1+中,这是不推荐使用的.而是after_saveafter_save回调中使用.

  • 谢谢你,我爱你. (14认同)
  • 这在Rails 5.1 +中已弃用.改为在`after_save`回调中使用`saved_changes` (9认同)
  • 当您不想使用模型回调并且需要在执行进一步功能之前进行有效保存时,这非常有效。 (3认同)
  • 要明确一点:在我的测试中(Rails 4),如果你使用`after_save`回调,`self.changed?`是`true`和`self.attribute_name_changed?`也是`true`,但是`self. previous_changes`返回一个空哈希. (3认同)

Fre*_*ang 59

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

我该怎么改变?

如果你attribute_changed?after_*-callbacks中使用方法,你会看到如下警告:

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

正如它提到的那样,你可以通过替换函数来轻松解决这个问题saved_change_to_attribute?.例如,name_changed?成为saved_change_to_name?.

同样,如果您使用the attribute_change来获取之前的值,这也会发生变化并抛出以下内容:

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

同样,正如它所提到的,该方法更改saved_change_to_attribute返回的名称["old", "new"].或者使用saved_changes,返回所有更改,这些更改可以作为saved_changes['attribute'].

  • 请注意,这个有用的响应还包括弃用`attribute_was`方法的解决方法:使用`saved_change_to_attribute`代替. (2认同)

zea*_*uss 47

如果您可以执行此操作before_save而不是after_save,您将能够使用此:

self.changed
Run Code Online (Sandbox Code Playgroud)

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

你也可以用:

self.changes
Run Code Online (Sandbox Code Playgroud)

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

  • 除了这些不能在`after_`回调中起作用,这就是问题的真正含义.@ jacek-głodek的答案是正确的. (8认同)

Jef*_*ran 7

"选定的"答案对我不起作用.我正在使用rails 3.1和CouchRest :: Model(基于Active Model).该_changed?方法不会在对更改的属性返回true after_update钩,只能在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
Run Code Online (Sandbox Code Playgroud)


ech*_*cho 6

您可以添加一个条件,after_update例如:

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

  ...
end
Run Code Online (Sandbox Code Playgroud)

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