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

时间:2010-10-05 07:52:18

标签: ruby-on-rails ruby-on-rails-4 model callback observer-pattern

我在模型观察器中设置了一个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

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

8 个答案:

答案 0 :(得分:166)

在模型的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

它只是有效。

答案 1 :(得分:147)

对于那些想要了解after_save回调中所做更改的人:

Rails 5.1及更高版本

model.saved_changes

Rails&lt; 5.1

model.previous_changes

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

答案 2 :(得分:46)

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

self.changed

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

你也可以使用:

self.changes

返回已更改的列的哈希值以及结果之前和之后的列数

答案 3 :(得分:45)

对于后来看到此内容的人,因为它目前(2017年8月)名列google:值得一提的是,此行为将在 Rails 5.2 中更改,并且在Rails中有弃用警告5.1,ActiveModel::Dirty改变了一点。

我该怎么做?

如果您在attribute_changed? - 回调中使用after_*方法,则会发出以下警告:

  

弃用警告:回调之后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?

同样,如果您使用attribute_change获取之前的值,则此更改也会发生以下情况:

  

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

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

答案 4 :(得分:7)

“选定”的答案对我不起作用。我正在使用rails 3.1和CouchRest :: Model(基于Active Model)。对于_changed?挂钩中的已更改属性,after_update方法不会返回true,仅在before_update挂钩中。我能够使用(new?)around_update hook:

来使用它
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

答案 5 :(得分:4)

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

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

  ...
end

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

答案 6 :(得分:0)

我正在使用它来提取具有新属性值的哈希值,这对我来说更新其他模型很有用

attributes_changed = self.changes.inject(Hash.new){|hash,attr| ((hash[attr[0].to_sym] = attr[1].last) || attr[1].last == false) && hash}

attr[1].last == false
当新值为false时,需要

,其中赋值返回false并且不返回“hash”。

我想有一种更简单的方法,我是rails的新手

答案 7 :(得分:-15)

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

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