Avec ActiveRecord, existe-t-il un moyen de récupérer les anciennes valeurs d’un enregistrement pendant after_update?

StackOverflow https://stackoverflow.com/questions/607069

Question

Configuration à l'aide d'un exemple simple: un tableau ( Totaux ) contient la somme de la colonne montant de chaque enregistrement. dans un deuxième tableau ( Things ).

Quand un thing.amount est mis à jour, j'aimerais simplement ajouter la différence entre l'ancienne valeur et la nouvelle valeur à total.sum .

Pour le moment, je soustrais self.amount pendant before_update et j'ajoute self.amount pendant after_update . Cela place beaucoup trop de confiance dans la réussite de la mise à jour.

Contrainte: je ne souhaite pas simplement recalculer la somme 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: J'y vais avec l'idée de Luke Francl. Pendant un rappel after_update , vous avez toujours accès aux valeurs self.attr_was , ce qui est exactement ce que je voulais. J'ai également décidé d'utiliser une implémentation after_update car je souhaite conserver ce type de logique dans le modèle. Ainsi, peu importe la façon dont je déciderai de mettre à jour les transactions à l'avenir, je saurai que je mets à jour correctement la somme des transactions. Merci à tous pour vos suggestions de mise en œuvre.

Était-ce utile?

La solution

Idem ce que tout le monde dit à propos des transactions.

Cela dit ...

ActiveRecord à partir de Rails 2.1 assure le suivi des valeurs d'attribut d'un objet. Ainsi, si vous avez un attribut total , vous aurez une méthode total_changed? et une méthode total_was qui renvoie l'ancienne valeur.

Il n'est pas nécessaire d'ajouter quoi que ce soit à votre modèle pour en suivre l'évolution.

Mise à jour: Voici la documentation pour ActiveModel :: Dirty à la demande.

Autres conseils

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

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

  

L'ensemble de la chaîne de rappel d'un appel de sauvegarde, de sauvegarde !, ou de destruction s'exécute dans une transaction. Cela inclut after_ * hooks. Si tout se passe bien, COMMIT est exécuté une fois la chaîne terminée.

     

Si un rappel before_ * annule l'action, un ROLLBACK est émis. Vous pouvez également déclencher un ROLLBACK en soulevant une exception dans n'importe lequel des callbacks, y compris after_ * hooks. Notez cependant que, dans ce cas, le client doit en être conscient, car une sauvegarde ordinaire lève cette exception au lieu de retourner silencieusement la valeur false.

Ajout de " _was " à votre attribut vous donnera la valeur précédente avant de sauvegarder les données.

Ces méthodes sont appelées méthodes incorrectes .

Salut!

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

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

ActiveRecord :: Dirty est un module intégré à ActiveRecord pour le suivi. attribuer des changements. Vous pouvez donc utiliser thing.amount_was pour obtenir l'ancienne valeur.

Ajoutez ceci à votre modèle:

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

Puis utilisez @old_amount dans votre code after_update.

Tout d'abord, vous devriez le faire dans 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 de before_update, à laquelle vous pourrez ensuite accéder dans after_update. Toutefois, cette solution n'est pas très élégante.

Idée 1: encapsulez la mise à jour dans une transaction de base de données, de sorte qu'en cas d'échec de la mise à jour, votre table de totaux ne soit pas modifiée: Documents ActiveRecord Transactions

Idée 2: Cachez l'ancienne valeur dans @old_total pendant before_update.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top