Dans les rails, comment puis-je savoir ce qui a causé l'échec d'un .save (), à part les erreurs de validation?

91

J'ai un modèle ActiveRecord qui revient truede valid?(et .errors est vide), mais qui revient falsede save(). Si l'instance de modèle est valide, comment puis-je savoir ce qui cause l'échec de l'enregistrement?

kdt
la source
7
J'ai eu ce problème il y a quelques semaines. Certaines refactorisations avaient laissé une fonction before_save retournant false tout le temps, ce qui provoquait l'échec de la sauvegarde.
Jeff Paquette
1
@Jeff - merci, il s'avère qu'il y avait une méthode: before_save retournant false. Comment avez-vous trouvé? Était-ce juste une inspection de code?
kdt le
C'était une inspection de code et des différences avec le contrôle de version.
Jeff Paquette

Réponses:

48

Vérifiez tous vos rappels.

J'ai eu un problème comme celui-ci où j'avais une méthode "after_validate" qui échouait après avoir apporté un tas de modifications au modèle. Le modèle était valide mais "after_validate" retournait false, donc si je l'utilisais, model.validil disait vrai, mais si je sauvegardais, cela me donnait des erreurs de validation (transmises par le rappel after_validate). C'était étrange.

Regardez la trace de l'application et vous devriez être en mesure de voir quelle ligne de code déclenche l'exception.

Andrew
la source
2
Selon le commentaire de Jeff, le problème s'est avéré être un rappel before_save retournant false.
kdt le
3
@kdt - c'est exactement ce qu'était mon problème. Je n'y avais pas pensé parce que before_save était juste destiné à définir une propriété, mais parce qu'il la définissait sur une valeur fausse, cela a été retourné implicitement et cela a fait échouer la sauvegarde en silence. Du bon côté, j'ai maintenant la possibilité de corriger ce code en ajoutant la ligne "Hey! That's MY fake leg!" # Believe it or not, this is important. Non pas que je ferais ça. ;)
Nathan Long
2
Un bon moyen d'assurer une vraie valeur de retour esttrue.tap { do_something }
Nathan Long
wow, quel problème obscur. Je n'aurais jamais deviné qu'un rappel retournant false aurait cessé de sauvegarder. Quelqu'un pourrait-il m'indiquer les documents à ce sujet? Merci de l'avoir signalé!
andy
114

Essayez d'utiliser la version bang save!(avec un point d'exclamation à la fin) et inspectez l'erreur qui en résulte.

Andy Lindeman
la source
4
enregistrer! lance juste un RecordNotSaved (lorsque j'imprime le .message de l'exception, j'obtiens juste le nom de la classe d'exception). Y a-t-il un endroit où je devrais chercher plus de détails?
kdt le
1
Si vous êtes en mode de développement Rails, il devrait afficher une description complète de l'erreur avec trace de pile. Jetez un œil là-bas pour des indices et / ou postez-le ici
Andy Lindeman
1
J'utilise la console, charge l'objet (par exemple, o = Object.find #id), puis fais o.save! comme le dit la réponse. Il imprime pourquoi il ne sauvegarde pas.
pduey
1
Pour info, l'appel save!peut augmenter ActiveRecord::RecordInvalid(car il exécute des validations) ou ActiveRecord::RecordNotSavedc'est ce que vous voudrez sauver.
Dennis
2
+1 car c'est la réponse la moins insatisfaisante à la question fondamentale de savoir comment diagnostiquer les .saveéchecs qui ne sont pas dus à la validation. La qualification «la moins insatisfaisante» se réfère à Rails, pas à cette réponse.
Chuck Batson
108

Si @user.save(par exemple) retourne false, exécutez simplement ceci pour obtenir toutes les erreurs:

@user.errors.full_messages
Sam Alex
la source
12
Comme je l'ai mentionné dans la question, .valid? est vrai - c'est-à-dire qu'il n'y a pas d'erreurs de validation. J'ai vérifié que .errors renvoie également une liste vide (j'ai mis à jour la question pour le signaler)
kdt
3

Oui, j'ai résolu ce problème en m'assurant que je renvoie true dans tous mes rappels before_ *, puis cela commence à fonctionner :)

Vérification au crayon
la source
-1

Le problème que j'ai eu était que j'avais oublié d'ajouter la validation au modèle.

class ContactGroup < ActiveRecord::Base
  validates_presence_of :name
end
Ian Vaughan
la source