ali*_*lik 48 ruby ruby-on-rails
我在我的应用程序中使用了after_commit.
我希望它只在我的模型中更新特定字段时触发.谁知道怎么做?
d_e*_*ier 69
老问题,但这是我发现的一种方法,可以使用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
}
Run Code Online (Sandbox Code Playgroud)
pau*_*kul 21
回答这个老问题,因为它仍会在搜索结果中弹出
你可以使用previous_changes方法返回格式的哈希值:
{"changed_attribute"=> ["旧值","新值"]}
这是在记录实际保存之前发生的变化(来自active_record/attribute_methods/dirty.rb):
def save(*) #:nodoc:
if status = super
@previously_changed = changes
@changed_attributes.clear
# .... whatever goes here
Run Code Online (Sandbox Code Playgroud)
所以在你的情况下你可以检查previous_changes.key? "your_attribute"或类似的东西
这是一个非常古老的问题,但公认的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
Run Code Online (Sandbox Code Playgroud)
其输出:
{"title"=>["1", "2"], "updated_at"=>[...]}
Run Code Online (Sandbox Code Playgroud)
但是,你需要的是:
{"title"=>["1", "2"], "number"=>[1, 2], "updated_at"=>[...]}
Run Code Online (Sandbox Code Playgroud)
所以,我的解决方案是这样的:
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
Run Code Online (Sandbox Code Playgroud)
您可以在这里尝试: https: //github.com/ccmcbeck/after-commit
我认为你无法做到 after_commit
在事务提交Rails事务之后调用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
Run Code Online (Sandbox Code Playgroud)
因此,您将无法使用:if条件,after_commit因为该属性将不再被标记为已更改,因为它已被保存.changed?在保存记录之前,您可能需要跟踪您所追踪的字段是否在另一个回调中?
老问题,但仍会出现在搜索结果中。
从 Rails 5attribute_changed?开始,不推荐使用。推荐使用saved_change_to_attribute?代替attribute_changed?。
听起来您想要类似条件回调之类的东西。如果您发布了一些代码,我可以为您指出正确的方向,但我认为您会想要使用这样的代码:
after_commit :callback,
:if => Proc.new { |record| record.field_modified? }
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
20346 次 |
| 最近记录: |