我在我的应用程序中使用了after_commit。
我希望只有在我的模型中更新特定字段时才会触发它。有谁知道怎么做?
答案 0 :(得分:69)
旧问题,但这是我发现的一种方法,可以使用after_commit回调(关闭paukul's answer)。至少,值都在IRB中持续提交后。
after_commit :callback,
if: proc { |record|
record.previous_changes.key?(:attribute) &&
record.previous_changes[:attribute].first != record.previous_changes[:attribute].last
}
答案 1 :(得分:21)
回答这个老问题,因为它仍会在搜索结果中弹出
您可以使用previous_changes方法返回格式的哈希值:
{“changed_attribute”=> [“旧值”,“新值”]}
这是changes直到记录实际保存的时间(来自active_record / attribute_methods / dirty.rb):
def save(*) #:nodoc:
if status = super
@previously_changed = changes
@changed_attributes.clear
# .... whatever goes here
所以在你的情况下,你可以检查previous_changes.key? "your_attribute"
或类似的东西
答案 2 :(得分:5)
我认为你不能在after_commit
在提交事务Rails Transactions
之后调用after_commit例如在我的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
因此,您无法在:if
上使用after_commit
条件,因为该属性在保存后不再被标记为已更改。在保存记录之前,您可能需要在另一个回调中跟踪您所追踪的字段是changed?
吗?
答案 3 :(得分:3)
听起来你想要像conditional callback这样的东西。如果你发布了一些代码,我可以指出你正确的方向,但我想你会想要使用这样的东西:
after_commit :callback,
:if => Proc.new { |record| record.field_modified? }
答案 4 :(得分:3)
旧问题,但仍会在搜索结果中弹出。
从Rails 5 attribute_changed?
开始已被取消。使用saved_change_to_attribute
吗?而不是attribute_changed
?建议。
答案 5 :(得分:1)
这是一个非常古老的问题,但接受的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
输出:
{"title"=>["1", "2"], "updated_at"=>[...]}
但是,你需要的是:
{"title"=>["1", "2"], "number"=>[1, 2], "updated_at"=>[...]}
所以,我的解决方案是:
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
答案 6 :(得分:0)
使用gem ArTransactionChanges。 previous_changes
在Rails 4.0.x中不适合我。
用法:
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