Используя ActiveRecord, есть способ получить старые значения записи во время after_update
Настройка с использованием простого примера: У меня есть 1 таблица (Totals
), которая содержит сумму столбца amount
каждой записи во второй таблице (Things
).
Когда обновляется thing.amount
, я хотел бы просто добавить разницу между старым значением и новым значением в total.sum
.
Сейчас я вычитаю self.amount
во время before_update
и добавляю self.amount
во время after_update
. Это приводит к тому, что WAY слишком сильно доверяет обновлению.
Ограничение: Я не хочу просто пересчитывать сумму всех транзакций.
Вопрос: Я просто хочу получить исходное значение во время обратного вызова after_update
. Какие у вас были способы сделать это?
Обновление: Я пойду с идеей Люка Франка. Во время обратного вызова after_update
у вас все еще есть доступ к значениям self.attr_was
, которые именно то, что я хотел. Я также решил пойти с реализацией after_update
, потому что хочу сохранить такую модель в модели. Таким образом, независимо от того, как я решил обновить транзакции в будущем, я буду знать, что я правильно обновляю сумму транзакций. Спасибо всем за ваши рекомендации по внедрению.
Ответы
Ответ 1
То же, что все говорят о транзакциях.
Это сказало...
ActiveRecord с Rails 2.1 отслеживает значения атрибута объекта. Поэтому, если у вас есть атрибут total
, у вас будет метод total_changed?
и метод total_was
, который возвращает старое значение.
Нет необходимости добавлять что-либо к вашей модели, чтобы больше не отслеживать это.
Обновление: Ниже приведена документация для ActiveModel:: Dirty в соответствии с запросом.
Ответ 2
Некоторые другие люди упоминают об упаковке всего этого в транзакции, но я думаю, что это сделано для вас; вам просто нужно вызвать откат путем создания исключения для ошибок в обратных вызовах after_ *.
См. http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
Целая цепочка обратного вызова вызова save, save!, или destroy запускается в транзакции. Это включает в себя after_ * hooks. Если все идет хорошо, COMMIT выполняется после завершения цепи.
Если обратный вызов before_ * отменяет действие, выдается ROLLBACK. Вы также можете запустить ROLLBACK, создавая исключение в любом из обратных вызовов, в том числе after_ * hooks. Обратите внимание, однако, что в этом случае клиент должен знать об этом, потому что обычное сохранение приведет к такому исключению, вместо того, чтобы спокойно возвращать false.
Ответ 3
Чтобы получить все измененные поля со старыми и новыми значениями соответственно:
person = Person.create!(:name => 'Bill')
person.name = 'Bob'
person.save
person.changes # => {"name" => ["Bill", "Bob"]}
Ответ 4
Добавление "_was" к вашему атрибуту даст вам предыдущее значение перед сохранением данных.
Эти методы называются грязные методы.
Ура!
Ответ 5
ActiveRecord:: Dirty - это модуль, встроенный в ActiveRecord для отслеживания изменений атрибутов. Таким образом, вы можете использовать thing.amount_was
для получения старого значения.
Ответ 6
Добавьте это в свою модель:
def amount=(new_value)
@old_amount = read_attribute(:amount)
write_attribute(:amount,new_value)
end
Затем используйте @old_amount в вашем коде after_update.
Ответ 7
Во-первых, вы должны сделать это в транзакции, чтобы обеспечить сбор ваших данных.
Чтобы ответить на ваш вопрос, вы можете просто установить переменную-член в старое значение в файле before_update, доступ к которому вы можете получить в after_update, однако это не очень изящное решение.
Ответ 8
Идея 1: оберните обновление в транзакцию базы данных, так что если обновление не завершится, ваша таблица Totals не будет изменена: Документы ActiveRecord Transactions
Идея 2: ставьте старое значение в @old_total во время before_update.