202 votes

Dans la méthode save() d'un modèle personnalisé de Django, comment identifier un nouvel objet ?

Je veux déclencher une action spéciale dans la méthode save() d'un objet modèle Django lorsque j'enregistre un nouvel enregistrement (et non pas lorsque je mets à jour un enregistrement existant).

La vérification de (self.id != None) est-elle nécessaire et suffisante pour garantir que l'enregistrement de self est nouveau et n'est pas en cours de mise à jour ? Y a-t-il des cas particuliers que cela pourrait négliger ?

1 votes

Veuillez sélectionner stackoverflow.com/a/35647389/8893667 comme réponse correcte. La réponse ne fonctionne pas dans de nombreux cas comme un UUIDField pk

227voto

Dave W. Smith Points 9470

Mis à jour : Avec la clarification que self._state n'est pas une variable d'instance privée, mais elle est nommée de cette façon pour éviter les conflits, en contrôlant self._state.adding est désormais le moyen de contrôle préférable.


self.pk is None:

renvoie True dans un nouvel objet Model, à moins que l'objet n'ait un objet UUIDField comme son primary_key .

Le cas particulier dont vous pourriez avoir à vous préoccuper est celui où il existe des contraintes d'unicité sur des champs autres que l'identifiant (par exemple, des index uniques secondaires sur d'autres champs). Dans ce cas, vous pourriez avoir un nouvel enregistrement en main, mais ne pas être en mesure de le sauvegarder.

0 votes

Le cas particulier de l'échec de la sauvegarde peut être traité en enveloppant l'appel super() dans un bloc try-except et en effectuant tout nettoyage nécessaire pour l'"action spéciale".

20 votes

Vous devez utiliser is not plutôt que != lors du contrôle d'identité avec le None objet

3 votes

Tous les modèles ne disposent pas d'un attribut id, c'est-à-dire qu'un modèle en étendant un autre par le biais d'un models.OneToOneField(OtherModel, primary_key=True) . Je pense que vous devez utiliser self.pk

49voto

Gerry Points 986

Vérification de self.id suppose que id est la clé primaire du modèle. Une manière plus générique serait d'utiliser la méthode raccourci pk .

is_new = self.pk is None

17 votes

Conseil de pro : mettez ça AVANT le site super(...).save() .

41voto

KayEss Points 1473

Le chèque de self.pk == None es no suffisante pour déterminer si l'objet va être inséré ou mis à jour dans la base de données.

L'O/RM de Django comporte un hack particulièrement méchant qui consiste essentiellement à vérifier s'il y a quelque chose à la position PK et, si c'est le cas, à faire un UPDATE, sinon à faire un INSERT (ceci est optimisé pour un INSERT si le PK est None).

La raison pour laquelle il doit faire cela est que vous êtes autorisé à définir le PK lorsqu'un objet est créé. Bien que cela ne soit pas courant lorsque vous avez une colonne de séquence pour la clé primaire, cela ne vaut pas pour les autres types de champs de clé primaire.

Si vous voulez vraiment savoir, vous devez faire ce que l'O/RM fait et regarder dans la base de données.

Bien sûr, vous avez un cas spécifique dans votre code et pour cela il est fort probable que self.pk == None vous dit tout ce que vous devez savoir, mais il est no une solution générale.

0 votes

Bon point ! Je peux m'en sortir dans mon application (vérification de l'absence de clé primaire) parce que je ne définis jamais la clé primaire pour les nouveaux objets. Mais ce ne serait certainement pas une bonne vérification pour un plugin réutilisable ou une partie du framework.

1 votes

Cela est particulièrement vrai lorsque vous attribuez la clé primaire vous-même et par l'intermédiaire de la base de données. Dans ce cas, la chose la plus sûre à faire est de faire un tour dans la base de données.

1 votes

Même si le code de votre application ne spécifie pas explicitement les pks, les fixtures de vos cas de test peuvent le faire. Cependant, comme ils sont généralement chargés avant les tests, cela peut ne pas être un problème.

12voto

JF Simon Points 783

Vous pourriez simplement vous connecter au signal post_save qui envoie un kwargs "created", si vrai, votre objet a été inséré.

http://docs.djangoproject.com/en/stable/ref/signals/#post-save

8 votes

Cela peut potentiellement causer des conditions de course s'il y a beaucoup de charge. C'est parce que le signal post_save est envoyé lors de la sauvegarde, mais avant que la transaction ne soit validée. Cela peut être problématique et rendre les choses très difficiles à déboguer.

0 votes

Je ne sais pas si les choses ont changé (depuis les anciennes versions), mais mes gestionnaires de signaux sont appelés au sein de la même transaction, de sorte qu'un échec à un endroit quelconque annule toute la transaction. J'utilise ATOMIC_REQUESTS donc je ne suis pas vraiment sûr de la valeur par défaut.

7voto

kannor Points 161

Vérifiez si self.id y el force_insert drapeau.

if not self.pk or kwargs.get('force_insert', False):
    self.created = True

# call save method.
super(self.__class__, self).save(*args, **kwargs)

#Do all your post save actions in the if block.
if getattr(self, 'created', False):
    # So something
    # Do something else

C'est pratique car votre objet nouvellement créé (self) le possède. pk valeur

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X