Есть ли способ получить старые значения записи во время after_update с помощью ActiveRecord?
-
03-07-2019 - |
Вопрос
Настройка на простом примере: У меня есть 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
реализацию, потому что я хочу сохранить такую логику в модели.Таким образом, независимо от того, как я решу обновить транзакции в будущем, я буду знать, что обновляю сумму транзакций правильно.Спасибо всем за предложения по реализации.
Решение
То же самое все говорят о транзакциях.
Тем не менее...
ActiveRecord начиная с Rails 2.1 отслеживает значения атрибутов объекта.Итак, если у вас есть атрибут total
, у вас будет total_changed?
метод и total_was
метод, который возвращает старое значение.
Больше не нужно ничего добавлять в модель, чтобы отслеживать это.
Обновлять: Вот документация для АктивМодель::Грязный как просили.
Другие советы
Некоторые другие люди упоминают о заключении всего этого в транзакцию, но я думаю, что для вас это уже сделано; вам просто нужно запустить откат, вызвав исключение для ошибок в обратных вызовах after_*.
Видеть http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
Вся цепочка обратных вызовов сохранения, сохранения! или уничтожения выполняется внутри транзакции.Это включает в себя хуки after_*.Если все идет нормально, после завершения цепочки выполняется COMMIT.
Если обратный вызов before_* отменяет действие, выдается ROLLBACK.Вы также можете вызвать ROLLBACK, вызывающий исключение в любом из обратных вызовов, включая перехватчики after_*.Однако обратите внимание, что в этом случае клиент должен знать об этом, потому что обычное сохранение вызовет такое исключение вместо того, чтобы незаметно вернуть false.
Добавление «_was» к вашему атрибуту даст вам предыдущее значение перед сохранением данных.
Эти методы называются грязные методы методы.
Ваше здоровье!
Чтобы получить все измененные поля со старыми и новыми значениями соответственно:
person = Person.create!(:name => 'Bill')
person.name = 'Bob'
person.save
person.changes # => {"name" => ["Bill", "Bob"]}
ActiveRecord::Грязный — это встроенный в ActiveRecord модуль для отслеживания изменений атрибутов.Таким образом, вы можете использовать thing.amount_was
чтобы получить старое значение.
Добавьте это в свою модель:
def amount=(new_value)
@old_amount = read_attribute(:amount)
write_attribute(:amount,new_value)
end
Затем используйте @old_amount в коде after_update.
Во-первых, вы должны делать это в транзакции, чтобы гарантировать, что ваши данные будут записаны вместе.
Чтобы ответить на ваш вопрос, вы можете просто установить для переменной-члена старое значение в before_update, к которому вы затем сможете получить доступ в after_update, однако это не очень элегантное решение.
Идея 1:Оберните обновление в транзакцию базы данных, чтобы в случае сбоя обновления ваша таблица итогов не изменилась: Документы по транзакциям ActiveRecord
Идея 2:Сохраните старое значение в @old_total во время before_update.