Ruby-on-rails after_commit 属性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7128073/
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
after_commit for an attribute
提问by alik
I am using an after_commit in my application.
我在我的应用程序中使用了 after_commit。
I would like it to trigger only when a particular field is updated in my model. Anyone know how to do that?
我希望它仅在我的模型中更新特定字段时触发。有谁知道怎么做?
回答by d_ethier
Old question, but this is one method that I've found that might work with the after_commit callback (working off paukul's answer). At least, the values both persist post-commit in IRB.
老问题,但这是我发现的一种方法,可能适用于 after_commit 回调(解决 paukul 的回答)。至少,这些值都在 IRB 提交后保持不变。
after_commit :callback,
if: proc { |record|
record.previous_changes.key?(:attribute) &&
record.previous_changes[:attribute].first != record.previous_changes[:attribute].last
}
回答by Pascal
Answering this old question because it still pops up in search results
回答这个老问题,因为它仍然会出现在搜索结果中
you can use the previous_changesmethod which returnes a hash of the format:
您可以使用previous_changes方法返回格式的哈希值:
{ "changed_attribute" => ["old value", "new value"] }
{ "changed_attribute" => ["旧值", "新值"] }
it's what changeswas until the record gets actually saved (from active_record/attribute_methods/dirty.rb):
这是在实际保存记录之前所做的更改(来自 active_record/attribute_methods/dirty.rb):
def save(*) #:nodoc:
if status = super
@previously_changed = changes
@changed_attributes.clear
# .... whatever goes here
so in your case you can check for previous_changes.key? "your_attribute"or something like that
所以在你的情况下,你可以检查previous_changes.key? "your_attribute"或类似的东西
回答by Paul.s
I don't think you can do it in after_commit
我不认为你可以做到 after_commit
The after_commit is called after the transaction is commited Rails Transactions
在提交事务后调用 after_commit Rails Transactions
For example in my rails console
例如在我的 rails 控制台中
> record = MyModel.find(1)
=> #<MyModel id: 1, label: "test", created_at: "2011-08-19 22:57:54", updated_at: "2011-08-19 22:57:54">
> record.label = "Changing text"
=> "Changing text"
> record.label_changed?
=> true
> record.save
=> true
> record.label_changed?
=> false
Therefore you won't be able to use the :ifcondition on after_commitbecause the attribute will not be marked as changed anymore as it has been saved. You may need to track whether the field you are after is changed?in another callback before the record is saved?
因此,您将无法使用:if条件 on,after_commit因为该属性在保存后将不再被标记为已更改。changed?在保存记录之前,您可能需要跟踪您所追求的字段是否在另一个回调中?
回答by sledge_909
Old question but still pops up in search results.
老问题,但仍会出现在搜索结果中。
As of Rails 5 attribute_changed?was deprecated. Using saved_change_to_attribute?instead of attribute_changed?is recommended.
从 Rails 5attribute_changed?开始,不推荐使用。推荐使用saved_change_to_attribute?代替attribute_changed?。
回答by Devin M
It sounds like you want something like a conditional callback. If you had posted some code I could have pointed you in the right direction however I think you would want to use something like this:
听起来您想要条件回调之类的东西。如果您发布了一些代码,我可以为您指出正确的方向,但是我认为您会想要使用这样的东西:
after_commit :callback,
:if => Proc.new { |record| record.field_modified? }
回答by Chris Beck
This is a very old problem, but the accepted previous_changessolution just isn't robust enough. In an ActiveRecordtransaction, there are many reasons why you might save a Model twice. previous_changesonly reflects the result of the final save. Consider this example
这是一个非常古老的问题,但公认的previous_changes解决方案还不够健壮。在ActiveRecord事务中,您可能会两次保存模型的原因有很多。previous_changes只反映最终的结果save。考虑这个例子
class Test < ActiveRecord::Base
after_commit: :after_commit_test
def :after_commit_test
puts previous_changes.inspect
end
end
test = Test.create(number: 1, title: "1")
test = Test.find(test.id) # to initialize a fresh object
test.transaction do
test.update(number: 2)
test.update(title: "2")
end
which outputs:
输出:
{"title"=>["1", "2"], "updated_at"=>[...]}
but, what you need is:
但是,您需要的是:
{"title"=>["1", "2"], "number"=>[1, 2], "updated_at"=>[...]}
So, my solution is this:
所以,我的解决方案是这样的:
module TrackSavedChanges
extend ActiveSupport::Concern
included do
# expose the details if consumer wants to do more
attr_reader :saved_changes_history, :saved_changes_unfiltered
after_initialize :reset_saved_changes
after_save :track_saved_changes
end
# on initalize, but useful for fine grain control
def reset_saved_changes
@saved_changes_unfiltered = {}
@saved_changes_history = []
end
# filter out any changes that result in the original value
def saved_changes
@saved_changes_unfiltered.reject { |k,v| v[0] == v[1] }
end
private
# on save
def track_saved_changes
# maintain an array of ActiveModel::Dirty.changes
@saved_changes_history << changes.dup
# accumulate the most recent changes
@saved_changes_history.last.each_pair { |k, v| track_saved_change k, v }
end
# v is an an array of [prev, current]
def track_saved_change(k, v)
if @saved_changes_unfiltered.key? k
@saved_changes_unfiltered[k][1] = track_saved_value v[1]
else
@saved_changes_unfiltered[k] = v.dup
end
end
# type safe dup inspred by http://stackoverflow.com/a/20955038
def track_saved_value(v)
begin
v.dup
rescue TypeError
v
end
end
end
which you can try out here: https://github.com/ccmcbeck/after-commit
你可以在这里尝试:https: //github.com/ccmcbeck/after-commit
回答by Simon Liu
Use gem ArTransactionChanges. previous_changesis not working for me in Rails 4.0.x
使用 gem ArTransactionChanges。previous_changes在 Rails 4.0.x 中对我不起作用
Usage:
用法:
class User < ActiveRecord::Base
include ArTransactionChanges
after_commit :print_transaction_changes
def print_transaction_changes
transaction_changed_attributes.each do |name, old_value|
puts "attribute #{name}: #{old_value.inspect} -> #{send(name).inspect}"
end
end
end

