En utilisant ActiveRecord, existe-t-il un moyen d’obtenir les anciennes valeurs d’un enregistrement pendant after_update?

Configuration à l’aide d’un exemple simple: J’ai une table ( Totals ) qui contient la sum de la colonne amount de chaque enregistrement dans une seconde table ( Things ).

Lorsqu’un object thing.amount est mis à jour, j’aimerais simplement append la différence entre l’ancienne valeur et la nouvelle valeur à total.sum .

En ce moment je soustrais self.amount pendant before_update et en ajoutant self.amount pendant after_update . Cela place trop de confiance dans la mise à jour réussie.

Contrainte: je ne veux pas simplement recalculer la sum de toutes les transactions.

Question: Tout simplement, j’aimerais accéder à la valeur d’origine lors d’un rappel after_update . Comment avez-vous trouvé cela?

Mise à jour: je vais avec l’idée de Luke Francl. Au cours d’un rappel after_update vous avez toujours access aux valeurs de self.attr_was , ce qui est exactement ce que je voulais. J’ai également décidé de procéder à une implémentation after_update car je souhaite conserver ce type de logique dans le modèle. De cette façon, peu importe la façon dont je décide de mettre à jour les transactions à l’avenir, je saurai que je mets à jour la sum des transactions correctement. Merci à tous pour vos suggestions d’implémentation.

Idem ce que tout le monde dit sur les transactions.

Cela dit…

ActiveRecord as from Rails 2.1 garde la trace des valeurs d’atsortingbut d’un object. Donc, si vous avez un atsortingbut total , vous aurez un total_changed? méthode et une méthode total_was qui renvoie l’ancienne valeur.

Il n’y a pas besoin d’append quoi que ce soit à votre modèle pour en garder la trace.

Mise à jour: Voici la documentation pour ActiveModel :: Dirty comme demandé.

D’autres personnes mentionnent envelopper tout cela dans une transaction, mais je pense que c’est fait pour vous; il vous suffit de déclencher la restauration en levant une exception pour les erreurs dans les rappels after_ *.

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

La chaîne de rappel complète d’un appel à la sauvegarde, à la sauvegarde ou à la destruction s’exécute dans une transaction. Cela inclut after_ * hooks. Si tout se passe bien, un COMMIT est exécuté une fois la chaîne terminée.

Si un rappel avant_ * annule l’action, un ROLLBACK est émis. Vous pouvez également déclencher un ROLLBACK générant une exception dans l’un des rappels, y compris les hooks after_ *. Notez toutefois que, dans ce cas, le client doit en être conscient car une sauvegarde ordinaire générera une telle exception au lieu de retourner discrètement la valeur false.

Pour obtenir tous les champs modifiés, avec leurs anciennes et nouvelles valeurs respectivement:

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

Ajouter “_was” à votre atsortingbut vous donnera la valeur précédente avant de sauvegarder les données.

Ces méthodes sont appelées méthodes de méthodes sales .

À votre santé!

ActiveRecord :: Dirty est un module intégré à ActiveRecord pour le suivi des modifications d’atsortingbuts. Vous pouvez donc utiliser thing.amount_was pour obtenir l’ancienne valeur.

Ajoutez ceci à votre modèle:

 def amount=(new_value) @old_amount = read_atsortingbute(:amount) write_atsortingbute(:amount,new_value) end 

Ensuite, utilisez @old_amount dans votre code after_update.

Tout d’abord, vous devez le faire lors d’une transaction pour vous assurer que vos données sont écrites ensemble.

Pour répondre à votre question, vous pouvez simplement définir une variable membre sur l’ancienne valeur dans before_update, à laquelle vous pouvez ensuite accéder dans after_update, mais cette solution n’est pas très élégante.

Idée 1: enveloppez la mise à jour dans une transaction de firebase database, de sorte que, si la mise à jour échoue, votre table Totals ne soit pas modifiée: docs ActiveRecord Transactions

Idée 2: Stash l’ancienne valeur dans @old_total lors de la before_update.