web-dev-qa-db-ger.com

Mit ActiveRecord können Sie die alten Werte eines Datensatzes während after_update abrufen

Einrichtung anhand eines einfachen Beispiels: Ich habe 1 Tabelle (Totals), die die Summe der amount -Spalte jedes Datensatzes in einer zweiten Tabelle enthält (Things).

Wenn ein thing.amount Aktualisiert wird, möchte ich einfach die Differenz zwischen dem alten und dem neuen Wert zu total.sum Hinzufügen.

Im Moment subtrahiere ich self.amount Während before_update Und füge self.amount Während after_update Hinzu. Dies setzt viel zu viel Vertrauen in das Update erfolgreich.

Einschränkung: Ich möchte nicht einfach die Summe aller Transaktionen neu berechnen.

Frage: Ganz einfach, ich möchte während eines after_update - Rückrufs auf den ursprünglichen Wert zugreifen. Auf welche Weise haben Sie sich das ausgedacht?

pdate: Ich gehe mit Luke Francls Idee. Während eines after_update - Rückrufs haben Sie immer noch Zugriff auf die self.attr_was - Werte, genau das, was ich wollte. Ich habe mich auch für eine after_update - Implementierung entschieden, weil ich diese Art von Logik im Modell behalten möchte. Auf diese Weise weiß ich, egal wie ich mich entscheide, Transaktionen in Zukunft zu aktualisieren, dass ich die Summe der Transaktionen korrekt aktualisiere. Vielen Dank an alle für Ihre Implementierungsvorschläge.

73
Abel

Das Gleiche gilt für das, was alle über Transaktionen sagen.

Das gesagt...

ActiveRecord ab Rails 2.1 verfolgt die Attributwerte eines Objekts. Wenn Sie also ein Attribut total haben, haben Sie ein total_changed? Methode und ein total_was Methode, die den alten Wert zurückgibt.

Sie müssen Ihrem Modell nichts mehr hinzufügen, um dies zu verfolgen.

Update: Hier ist die Dokumentation für ActiveModel :: Dirty wie angefordert.

141
Luke Francl

Wenn Sie "_was" an Ihr Attribut anhängen, erhalten Sie den vorherigen Wert, bevor Sie die Daten speichern.

Diese Methoden heißen Dirty-Methoden Methoden.

Prost!

10

Einige andere Leute erwähnen, dass sie all dies in eine Transaktion einwickeln, aber ich denke, das ist für Sie erledigt. Sie müssen nur das Rollback auslösen, indem Sie eine Ausnahme für Fehler in den after_ * -Rückrufen auslösen.

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

Die gesamte Rückrufkette eines Save-, Save- oder Destroy-Aufrufs wird innerhalb einer Transaktion ausgeführt. Das schließt after_ * Hooks ein. Wenn alles gut geht, wird ein COMMIT ausgeführt, sobald die Kette abgeschlossen ist.

Wenn ein before_ * Rückruf die Aktion abbricht, wird ein ROLLBACK ausgegeben. Sie können auch einen ROLLBACK auslösen, der eine Ausnahme in einem der Rückrufe auslöst, einschließlich after_ * -Hooks. Beachten Sie jedoch, dass der Client in diesem Fall darüber informiert sein muss, da ein gewöhnlicher Speichervorgang eine solche Ausnahme auslöst, anstatt stillschweigend false zurückzugeben.

9
Gabe Hollombe

So rufen Sie alle geänderten Felder mit ihren alten bzw. neuen Werten ab:

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

ActiveRecord :: Dirty ist ein in ActiveRecord integriertes Modul zum Nachverfolgen von Attributänderungen. So können Sie thing.amount_was, um den alten Wert zu erhalten.

4
John Topley

Fügen Sie dies Ihrem Modell hinzu:

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

Verwenden Sie dann @old_amount in Ihrem after_update-Code.

3

Erstens sollten Sie dies in einer Transaktion tun, um sicherzustellen, dass Ihre Daten zusammengeschrieben werden.

Um Ihre Frage zu beantworten, können Sie einfach eine Mitgliedsvariable auf den alten Wert in before_update setzen, auf den Sie dann in after_update zugreifen können. Dies ist jedoch keine sehr elegante Lösung.

0
jonnii

Idee 1: Binden Sie das Update in eine Datenbanktransaktion ein, damit Ihre Summentabelle nicht geändert wird, wenn das Update fehlschlägt: ActiveRecord Transactions docs

Idee 2: Verstecken Sie den alten Wert in @old_total während des before_updates.

0
bradheintz