Подтвердить что ты не робот

After_commit для атрибута

Я использую after_commit в своем приложении.

Я хотел бы, чтобы он срабатывал только тогда, когда определенное поле обновлялось в моей модели. Кто-нибудь знает, как это сделать?

4b9b3361

Ответ 1

Старый вопрос, но это один из методов, который я нашел, который мог бы работать с обратным вызовом after_commit (отключение paukul answer). По крайней мере, значения сохраняются после фиксации в IRB.

after_commit :callback, 
  if: proc { |record| 
    record.previous_changes.key?(:attribute) &&
      record.previous_changes[:attribute].first != record.previous_changes[:attribute].last
  }

Ответ 2

Отвечая на этот старый вопрос, потому что он все еще появляется в результатах поиска

вы можете использовать метод previous_changes, который возвращает хэш формата:

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

это то, что 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" или что-то подобное

Ответ 3

Я не думаю, что вы можете сделать это в after_commit

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? поле, за которым вы работаете changed? в другом обратном вызове до сохранения записи?

Ответ 4

Похоже, вы хотите что-то вроде условного обратного вызова. Если бы вы разместили какой-то код, я мог бы указать вам в правильном направлении, но я думаю, вы бы хотели использовать что-то вроде этого:

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

Ответ 5

Старый вопрос, но все еще появляется в результатах поиска.

Начиная с Rails 5 attribute_changed? устарела. Рекомендуется использовать saved_change_to_attribute? вместо attribute_changed?.

Ответ 6

Это очень старая проблема, но принятое решение 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

Ответ 7

Используйте gem 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