after_commit属性

时间:2011-08-19 22:31:16

标签: ruby-on-rails ruby

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

我希望只有在我的模型中更新特定字段时才会触发它。有谁知道怎么做?

7 个答案:

答案 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

你可以在这里试试:https://github.com/ccmcbeck/after-commit

答案 6 :(得分:0)

使用gem ArTransactionChangesprevious_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