after_commit属性

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)

  • `record.previous_changes.key?(:attribute)`是足够的条件,因为第一个和最后一个值总是不相等. (14认同)
  • @jamesdevar是正确的,但如果您的模型在事务期间保存了多次,则此解决方案可能会失败.下面是一个示例Rails项目https://github.com/ccmcbeck/after-commit来演示问题以及解决问题的解决方案. (2认同)
  • 对于想要检查记录是否在多个属性上更改的人:`(record.previous_changes.keys&[:list,:of,:attributes]).any? (2认同)

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"或类似的东西


Chr*_*eck 8

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


Pau*_*l.s 5

我认为你无法做到 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?在保存记录之前,您可能需要跟踪您所追踪的字段是否在另一个回调中?


sle*_*909 5

老问题,但仍会出现在搜索结果中。

从 Rails 5attribute_changed?开始,不推荐使用。推荐使用saved_change_to_attribute?代替attribute_changed?


Dev*_*n M 4

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

after_commit :callback,
  :if => Proc.new { |record| record.field_modified? }
Run Code Online (Sandbox Code Playgroud)