Используя ActiveRecord, есть ли способ получить старые значения записи во время after_update? - PullRequest
71 голосов
/ 03 марта 2009

Настройка на простом примере: У меня есть 1 таблица (Totals), в которой содержится сумма столбца amount каждой записи во второй таблице (Things).

Когда обновляется thing.amount, я бы просто хотел добавить разницу между старым значением и новым значением к total.sum.

Прямо сейчас я вычитаю self.amount во время before_update и добавляю self.amount во время after_update. Это ставит слишком много доверия к успешному обновлению.

Ограничение: Я не хочу просто пересчитывать сумму всех транзакций.

Вопрос: Проще говоря, я хотел бы получить доступ к исходному значению во время обратного вызова after_update. Какими способами вы пришли к этому?

Обновление: Я иду с идеей Люка Франкла. Во время обратного вызова after_update у вас все еще есть доступ к значениям self.attr_was, что именно то, что я хотел. Я также решил пойти с реализацией after_update, потому что я хочу сохранить такую ​​логику в модели. Таким образом, независимо от того, как я решу обновить транзакции в будущем, я буду знать, что я корректно обновляю сумму транзакций. Спасибо всем за ваши предложения по реализации.

Ответы [ 8 ]

138 голосов
/ 03 марта 2009

То же самое говорят все о транзакциях.

Это говорит ...

ActiveRecord с Rails 2.1 отслеживает значения атрибутов объекта. Поэтому, если у вас есть атрибут total, у вас будет метод total_changed? и метод total_was, который возвращает старое значение.

Нет необходимости добавлять что-либо в вашу модель, чтобы отслеживать это.

Обновление: Вот документация для ActiveModel :: Dirty согласно запросу.

9 голосов
/ 02 июля 2014

Добавление "_was" к вашему атрибуту даст вам предыдущее значение перед сохранением данных.

Эти методы называются грязные методы методы.

Ура!

9 голосов
/ 03 марта 2009

Некоторые другие люди упоминают об упаковке всего этого в транзакции, но я думаю, что это сделано для вас; вам просто нужно вызвать откат, вызвав исключение для ошибок в обратных вызовах after_ *.

См. http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

Вся цепочка обратного вызова вызова save, save !, или destroy выполняется внутри транзакции. Это включает в себя after_ * хуки. Если все идет хорошо, выполняется COMMIT после завершения цепочки.

Если обратный вызов before_ * отменяет действие, выдается ROLLBACK. Вы также можете вызвать ROLLBACK, вызывая исключение в любом из обратных вызовов, включая ловушки after_ *. Тем не менее, обратите внимание, что в этом случае клиент должен знать об этом, потому что обычное сохранение вызовет такое исключение вместо тихого возврата false

7 голосов
/ 05 августа 2013

Чтобы получить все измененные поля с их старыми и новыми значениями соответственно:

person = Person.create!(:name => 'Bill')
person.name = 'Bob'
person.save
person.changes        # => {"name" => ["Bill", "Bob"]}
4 голосов
/ 03 марта 2009

ActiveRecord :: Dirty - это модуль, который встроен в ActiveRecord для отслеживания изменений атрибутов. Таким образом, вы можете использовать thing.amount_was, чтобы получить старое значение.

2 голосов
/ 03 марта 2009

Добавьте это к вашей модели:

def amount=(new_value)
    @old_amount = read_attribute(:amount)
    write_attribute(:amount,new_value)
end

Затем используйте @old_amount в своем коде after_update.

0 голосов
/ 03 марта 2009

Идея 1. Оберните обновление в транзакцию базы данных, чтобы в случае сбоя обновления ваша таблица итогов не изменялась: Документы транзакций ActiveRecord

Идея 2. Сохраните старое значение в @old_total во время before_update.

0 голосов
/ 03 марта 2009

Во-первых, вы должны сделать это в транзакции, чтобы гарантировать, что ваши данные записываются вместе.

Чтобы ответить на ваш вопрос, вы можете просто установить для переменной-члена старое значение в before_update, к которому затем можно получить доступ в after_update, однако это не очень элегантное решение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...