Używając ActiveRecord, czy istnieje sposób na uzyskanie starych wartości rekordu podczas after_update


81

Konfiguracja za pomocą prostego przykładu: Mam 1 tabelę ( Totals), która przechowuje sumę amountkolumny każdego rekordu w drugiej tabeli ( Things).

Kiedy a thing.amountzostanie zaktualizowany, chciałbym po prostu dodać różnicę między starą wartością a nową wartością do total.sum.

W tej chwili odejmuję w self.amounttrakcie before_updatei dodam w self.amounttrakcie after_update. To zbytnie pokłada zaufanie w powodzeniu aktualizacji.

Ograniczenie: nie chcę po prostu ponownie obliczać sumy wszystkich transakcji.

Pytanie: Po prostu chciałbym uzyskać dostęp do oryginalnej wartości podczas after_updatewywołania zwrotnego. Jakie sposoby wymyśliłeś, aby to zrobić?

Aktualizacja: idę z pomysłem Luke'a Francla. Podczas after_updateoddzwonienia nadal masz dostęp do self.attr_waswartości, które są dokładnie takie, jakie chciałem. Zdecydowałem się również na after_updateimplementację, ponieważ chcę zachować tego rodzaju logikę w modelu. Dzięki temu bez względu na to, jak zdecyduję się aktualizować transakcje w przyszłości, będę wiedział, że poprawnie aktualizuję sumę transakcji. Dziękujemy wszystkim za sugestie dotyczące wdrożenia.

Odpowiedzi:


149

Podobnie jak wszyscy mówią o transakcjach.

To mówi...

ActiveRecord od wersji Rails 2.1 śledzi wartości atrybutów obiektu. Więc jeśli masz atrybut total, będziesz mieć total_changed?metodę i total_wasmetodę, która zwraca starą wartość.

Nie musisz już niczego dodawać do swojego modelu, aby to śledzić.

Aktualizacja: Oto dokumentacja dla ActiveModel :: Dirty zgodnie z żądaniem.


Myślałem, że coś takiego istnieje. Czy możesz podać link do odpowiedniej dokumentacji dotyczącej attr_changed? i attr_was?
Gabe Hollombe

Jasne, dodałem link do odpowiedzi.
Luke Francl

10

Niektórzy wspominają o zawarciu tego wszystkiego w transakcji, ale myślę, że to zrobiono dla ciebie; wystarczy wyzwolić wycofywanie, zgłaszając wyjątek dla błędów w wywołaniach zwrotnych after_ *.

Zobacz http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

Cały łańcuch wywołań zwrotnych zapisywania, zapisywania! Lub niszczenia przebiega w ramach transakcji. Obejmuje to haki after_ *. Jeśli wszystko pójdzie dobrze, po zakończeniu łańcucha wykonywany jest COMMIT.

Jeśli wywołanie zwrotne before_ * anuluje akcję, wykonywany jest ROLLBACK. Możesz również wyzwolić ROLLBACK, podnosząc wyjątek w dowolnym wywołaniu zwrotnym, w tym przechwyceniu after_ *. Należy jednak pamiętać, że w takim przypadku klient musi być tego świadomy, ponieważ zwykłe zapisanie wywoła taki wyjątek zamiast po cichu zwrócić fałsz.



8

Aby uzyskać wszystkie zmienione pola, odpowiednio ze starymi i nowymi wartościami:

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

5
Uważam, że ostatnia linia powinna być teraz czytana person.previous_changeszamiastperson.changes
Jason Axelson,


3

Dodaj to do swojego modelu:

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

Następnie użyj @old_amount w swoim kodzie after_update.


0

Po pierwsze, powinieneś to zrobić w transakcji, aby upewnić się, że Twoje dane zostaną zapisane razem.

Aby odpowiedzieć na twoje pytanie, możesz po prostu ustawić zmienną składową na starą wartość w before_update, do której możesz uzyskać dostęp w after_update, jednak nie jest to zbyt eleganckie rozwiązanie.


Staram się zobaczyć, jak zrealizowałbym to, co chcę, za pomocą transakcji. Czy taka transakcja istnieje w modelu czy w kontrolerze? Czy mogę usunąć moje wywołania zwrotne „after_update” i „before_update”? Wreszcie, jak uzyskać starą wartość potrzebną do określenia różnicy?
Abel

nieważne, widzę, że muszę umieścić kod w kontrolerze. były dobre.
Abel


0

Jeśli chcesz uzyskać wartość danego pola po aktualizacji możesz skorzystać z metody 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)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.