Ruby on Rails-属性的after_commit

我在我的应用程序中使用了after_commit。

我希望仅在模型中更新特定字段时才触发它。 有人知道该怎么做吗?

alik asked 2020-02-18T18:21:10Z
7个解决方案
69 votes

旧问题,但这是我发现的一种方法,可以与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
  }
d_ethier answered 2020-02-18T18:21:23Z
21 votes

回答这个旧问题,因为它仍然会出现在搜索结果中

您可以使用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

因此您可以检查一下previous_changes.key? "your_attribute"或类似的东西

paukul answered 2020-02-18T18:22:01Z
5 votes

我认为您无法在:if中做到

提交事务后调用after_commit Rails Transactions

例如在我的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 

因此,您将无法在after_commit上使用:if条件,因为该属性已保存,因此不再标记为已更改。 您可能需要在保存记录之前在另一个回调中跟踪您追寻的字段是否是changed?

Paul.s answered 2020-02-18T18:22:35Z
4 votes

旧问题,但仍会在搜索结果中弹出。

从Rails 5开始,已弃用attribute_changed?。 建议使用saved_change_to_attribute?代替attribute_changed?

sledge_909 answered 2020-02-18T18:22:59Z
3 votes

听起来您想要条件回调之类的东西。 如果您发布了一些代码,我本可以为您指明正确的方向,但是我认为您会想使用以下代码:

after_commit :callback,
  :if => Proc.new { |record| record.field_modified? }
Devin M answered 2020-02-18T18:23:19Z
1 votes

这是一个非常老的问题,但是公认的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

您可以在这里尝试:[https://github.com/ccmcbeck/after-commit]

Chris Beck answered 2020-02-18T18:23:57Z
0 votes

使用宝石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
Simon Liu answered 2020-02-18T18:24:21Z
translate from https://stackoverflow.com:/questions/7128073/after-commit-for-an-attribute