Ruby-on-rails 确定在 Rails after_save 回调中更改了哪些属性?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3861777/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Determine what attributes were changed in Rails after_save callback?
提问by modulaaron
I'm setting up an after_save callback in my model observer to send a notification only if the model's publishedattribute was changed from false to true. Since methods such as changed?are only useful before the model is saved, the way I'm currently (and unsuccessfully) trying to do so is as follows:
我正在我的模型观察器中设置 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
Does anyone have any suggestions as to the best way to handle this, preferably using model observer callbacks (so as not to pollute my controller code)?
有没有人对处理这个问题的最佳方法有任何建议,最好使用模型观察者回调(以免污染我的控制器代码)?
回答by Jacek G?odek
For those who want to know the changes just made in an after_savecallback:
对于那些想知道刚刚在after_save回调中所做的更改的人:
Rails 5.1 and greater
Rails 5.1 及更高版本
model.saved_changes
Rails < 5.1
导轨 < 5.1
model.previous_changes
Also see: http://api.rubyonrails.org/classes/ActiveModel/Dirty.html#method-i-previous_changes
另请参阅:http: //api.rubyonrails.org/classes/ActiveModel/Dirty.html#method-i-previous_changes
回答by Radek Paviensky
Rails 5.1+
导轨 5.1+
Use saved_change_to_published?:
使用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
Or if you prefer, saved_change_to_attribute?(:published).
或者,如果您愿意,saved_change_to_attribute?(:published).
Rails 3–5.1
导轨 3–5.1
Warning
This approach works through Rails 5.1 (but is deprecated in 5.1 and has breaking changes in 5.2). You can read about the change in this pull request.
警告
此方法适用于 Rails 5.1(但在 5.1 中已弃用,并在 5.2 中进行了重大更改)。您可以阅读此拉取请求中的更改。
In your after_updatefilter on the model you canuse _changed?accessor. So for example:
在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
It just works.
它只是有效。
回答by Frederik Spang
To anyone seeing this later on, as it currently (Aug. 2017) tops google: It is worth mentioning, that this behavior will be altered in Rails 5.2, and has deprecation warnings as of Rails 5.1, as ActiveModel::Dirtychanged a bit.
对于稍后看到这一点的任何人,因为它目前(2017 年 8 月)在 google 上名列前茅:值得一提的是,这种行为将在Rails 5.2 中改变,并且从 Rails 5.1 开始有弃用警告,因为ActiveModel::Dirty发生了一些变化.
What do I change?
我要改变什么?
If you're using attribute_changed?method in the after_*-callbacks, you'll see a warning like:
如果您attribute_changed?在after_*-callbacks 中使用方法,您将看到如下警告:
DEPRECATION WARNING: The behavior of
attribute_changed?inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method aftersavereturned (e.g. the opposite of what it returns now). To maintain the current behavior, usesaved_change_to_attribute?instead. (called from some_callback at /PATH_TO/app/models/user.rb:15)
弃用警告:
attribute_changed?在下一个版本的 Rails 中,内部回调的行为将发生变化。新的返回值将反映save返回后调用方法的行为(例如,与现在返回的相反)。要保持当前行为,请saved_change_to_attribute?改用。(从 /PATH_TO/app/models/user.rb:15 处的 some_callback 调用)
As it mentions, you could fix this easily by replacing the function with saved_change_to_attribute?. So for example, name_changed?becomes saved_change_to_name?.
正如它所提到的,您可以通过将函数替换为saved_change_to_attribute?. 因此,例如,name_changed?变为saved_change_to_name?。
Likewise, if you're using the attribute_changeto get the before-after values, this changes as well and throws the following:
同样,如果您使用attribute_change来获取前后值,这也会发生变化并抛出以下内容:
DEPRECATION WARNING: The behavior of
attribute_changeinside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method aftersavereturned (e.g. the opposite of what it returns now). To maintain the current behavior, usesaved_change_to_attributeinstead. (called from some_callback at /PATH_TO/app/models/user.rb:20)
弃用警告:
attribute_change在下一个版本的 Rails 中,内部回调的行为将发生变化。新的返回值将反映save返回后调用方法的行为(例如,与现在返回的相反)。要保持当前行为,请saved_change_to_attribute改用。(从 /PATH_TO/app/models/user.rb:20 处的 some_callback 调用)
Again, as it mentions, the method changes name to saved_change_to_attributewhich returns ["old", "new"].
or use saved_changes, which returns all the changes, and these can be accessed as saved_changes['attribute'].
同样,正如它所提到的,该方法将 name 更改为saved_change_to_attributereturn ["old", "new"]。或使用saved_changes,它返回所有更改,这些可以作为 访问saved_changes['attribute']。
回答by zeacuss
In case you can do this on before_saveinstead of after_save, you'll be able to use this:
如果您可以在before_save而不是 上执行此操作after_save,您将能够使用它:
self.changed
it returns an array of all changed columns in this record.
它返回此记录中所有更改列的数组。
you can also use:
您还可以使用:
self.changes
which returns a hash of columns that changed and before and after results as arrays
它以数组形式返回更改前后的列的哈希值
回答by Jeff Gran
The "selected" answer didn't work for me. I'm using rails 3.1 with CouchRest::Model (based on Active Model). The _changed?methods don't return true for changed attributes in the after_updatehook, only in the before_updatehook. I was able to get it to work using the (new?) around_updatehook:
“选定”的答案对我不起作用。我将 rails 3.1 与 CouchRest::Model (基于活动模型)一起使用。_changed?对于after_update钩子中已更改的属性,这些方法不会返回 true ,仅在before_update钩子中。我能够使用(新的?)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
回答by echo
you can add a condition to the after_updatelike so:
您可以添加一个条件,after_update例如:
class SomeModel < ActiveRecord::Base
after_update :send_notification, if: :published_changed?
...
end
there's no need to add a condition within the send_notificationmethod itself.
无需在send_notification方法本身中添加条件。
回答by shingara
You just add an accessor who define what you change
您只需添加一个访问者来定义您更改的内容
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

