Используя 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.