Ruby-on-rails after_commit 属性

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7128073/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 01:48:55  来源:igfitidea点击:

after_commit for an attribute

ruby-on-railsruby

提问by alik

I am using an after_commit in my application.

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

I would like it to trigger only when a particular field is updated in my model. Anyone know how to do that?

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

回答by d_ethier

Old question, but this is one method that I've found that might work with the after_commit callback (working off paukul's answer). At least, the values both persist post-commit in IRB.

老问题,但这是我发现的一种方法,可能适用于 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
  }

回答by Pascal

Answering this old question because it still pops up in search results

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

you can use the previous_changesmethod which returnes a hash of the format:

您可以使用previous_changes方法返回格式的哈希值:

{ "changed_attribute" => ["old value", "new value"] }

{ "changed_attribute" => ["旧值", "新值"] }

it's what changeswas until the record gets actually saved (from active_record/attribute_methods/dirty.rb):

这是在实际保存记录之前所做的更改(来自 active_record/attribute_methods/dirty.rb):

  def save(*) #:nodoc:
    if status = super
      @previously_changed = changes
      @changed_attributes.clear
      # .... whatever goes here

so in your case you can check for previous_changes.key? "your_attribute"or something like that

所以在你的情况下,你可以检查previous_changes.key? "your_attribute"或类似的东西

回答by Paul.s

I don't think you can do it in after_commit

我不认为你可以做到 after_commit

The after_commit is called after the transaction is commited Rails Transactions

在提交事务后调用 after_commit Rails Transactions

For example in my rails console

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

Therefore you won't be able to use the :ifcondition on after_commitbecause the attribute will not be marked as changed anymore as it has been saved. You may need to track whether the field you are after is changed?in another callback before the record is saved?

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

回答by sledge_909

Old question but still pops up in search results.

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

As of Rails 5 attribute_changed?was deprecated. Using saved_change_to_attribute?instead of attribute_changed?is recommended.

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

回答by Devin M

It sounds like you want something like a conditional callback. If you had posted some code I could have pointed you in the right direction however I think you would want to use something like this:

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

after_commit :callback,
  :if => Proc.new { |record| record.field_modified? }

回答by Chris Beck

This is a very old problem, but the accepted previous_changessolution just isn't robust enough. In an ActiveRecordtransaction, there are many reasons why you might save a Model twice. previous_changesonly reflects the result of the final save. Consider this example

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

which outputs:

输出:

{"title"=>["1", "2"], "updated_at"=>[...]}

but, what you need is:

但是,您需要的是:

{"title"=>["1", "2"], "number"=>[1, 2], "updated_at"=>[...]}

So, my solution is this:

所以,我的解决方案是这样的:

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

which you can try out here: https://github.com/ccmcbeck/after-commit

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

回答by Simon Liu

Use gem ArTransactionChanges. previous_changesis not working for me in Rails 4.0.x

使用 gem ArTransactionChangesprevious_changes在 Rails 4.0.x 中对我不起作用

Usage:

用法:

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