Використовуючи ActiveRecord, чи є спосіб отримати старі значення запису під час after_update


81

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

Коли a thing.amountоновлюється, я хотів би просто додати різницю між старим і новим значенням total.sum.

Зараз я віднімаю self.amountпід час before_updateі додаю self.amountпід час after_update. Це занадто багато довіряє успішному оновленню.

Обмеження: Я не хочу просто перерахувати суму всіх операцій.

Питання: Просто, я хотів би отримати доступ до вихідного значення під час after_updateзворотного дзвінка. Які способи ви придумали для цього?

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

Відповіді:


149

Так само, що всі говорять про транзакції.

Тим не менше ...

ActiveRecord станом на Rails 2.1 відстежує значення атрибутів об’єкта. Отже, якщо у вас є атрибут total, у вас буде total_changed?метод і total_wasметод, що повертає старе значення.

Не потрібно нічого додавати до своєї моделі, щоб більше це відстежувати.

Оновлення: Ось документація до ActiveModel :: Dirty за запитом.


Я думав, що щось подібне існує. Чи можете ви зробити посилання на відповідну документацію про attr_changed? і attr_was?
Гейб Холломб

Звичайно, я додав посилання на відповідь.
Luke Francl

10

Деякі люди згадують про обгортання всього цього в транзакції, але я думаю, що це зроблено для вас; вам просто потрібно ініціювати відкат, викликаючи виняток для помилок у зворотних викликах after_ *.

Див. Http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

Весь ланцюжок зворотного виклику збереження, збереження! Або знищення виклику працює в межах транзакції. Це включає гачки after_ *. Якщо все йде нормально, після завершення ланцюжка виконується COMMIT.

Якщо зворотний виклик before_ * скасовує дію, видається ROLLBACK. Ви також можете викликати ROLLBACK, що викликає виняток, у будь-якому із зворотних викликів, включаючи хуки after_ *. Однак зауважте, що в такому випадку клієнт повинен це знати, оскільки звичайне збереження спричинить такий виняток, а не тихо поверне false.



8

Щоб отримати всі змінені поля з їх старими та новими значеннями відповідно:

person = Person.create!(:name => 'Bill')
person.name = 'Bob'
person.save
person.changes        # => {"name" => ["Bill", "Bob"]}

5
Я вважаю, що останній рядок тепер слід читати person.previous_changesзамістьperson.changes
Джейсон Аксельсон

5

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


3

Додайте це до своєї моделі:

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

Потім використовуйте @old_amount у коді після оновлення.


1

Якщо ви хочете отримати значення конкретного поля після оновлення, ви можете використовувати метод field_before_last_save.

Example:

u = User.last
u.update(name: "abc")

u.name_before_last_save will give you old value (before update value)

0

По-перше, ви повинні робити це під час транзакції, щоб забезпечити запис ваших даних.

Щоб відповісти на ваше запитання, ви можете просто встановити для змінної-члена старе значення в перед_оновленням, до якого ви потім можете отримати доступ після_оновлення, однак це не дуже елегантне рішення.


Я намагаюся побачити, як би я реалізував те, що хочу, використовуючи транзакцію. Така транзакція живе в моделі або в контролері? Чи видаляти мої зворотні виклики 'after_update' та 'before_update'? Нарешті, як мені отримати старе значення, яке мені потрібно для того, щоб визначити різницю?
Абель

нічого, я бачу, що я повинен помістити код в контролер. ми добре.
Абель

0

Ідея 1. Оберніть оновлення транзакцією бази даних, щоб у разі невдалого оновлення таблиця підсумків не змінювалась: ActiveRecord Transaction docs

Ідея 2: Зберігайте старе значення в @old_total під час before_update.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.