2009-11-27 7 views
0

En django, je suis en train de faire quelque chose comme ceci:Pourquoi dois-je enregistrer ce modèle avant de l'ajouter à un autre?

# if form is valid ... 
article = form.save(commit=False) 
article.author = req.user 

product_name = form.cleaned_data['product_name'] 
try: 
    article.product = Component.objects.get(name=product_name) 
except: 
    article.product = Component(name=product_name) 

article.save() 
# do some more form processing ... 

Mais il me dit:

valeur NULL dans la colonne "product_id" viole contrainte non nulle

Mais je ne comprends pas pourquoi c'est un problème. Lorsque article.save() est appelé, il devrait pouvoir créer le produit puis (et générer un identifiant).

Je peux contourner ce problème en utilisant ce code dans le bloc except:

product = Component(name=product_name) 
product.save() 
article.product = product 

Mais la raison pour laquelle cela me préoccupe est parce que si article.save() échoue, il aura déjà créé un nouveau composant/produit. Je veux qu'ils réussissent ou échouent ensemble.

Existe-t-il un bon moyen de contourner ce problème?

+1

Remarque: article.product = Component.objects.create (nom = nom_du_produit) est un peu plus ordonné – michael

+0

@michael: Oh!Je ne savais pas que je pouvais faire ça. C'est au moins un peu mieux. – mpen

Répondre

1

Vous pouvez contourner ce problème en utilisant:

target_product, created_flag = Component.objects.get_or_create(name=product_name) 
article.product = target_product 

que je suis assez sûr get_or_create() va régler l'id d'un objet, si elle doit créer un. Sinon, si les relations FK vides sur la table article ne vous dérangent pas, vous pouvez ajouter null=True à la définition.

+0

Et si je ne me soucie pas du 'created_flag'? Y a-t-il un peu de syntaxe python qui me permet d'omettre ça? Peut-être une virgule, mais pas de 2e val? – mpen

+0

L'underscore est assez commun: target_product, _ = Component.object.get_or_create (...) J'utilise le trait de soulignement double, je réserve un trait de soulignement simple pour les fonctions i18n. –

+0

Ce soulignement est assez moche, vous ne pensez pas? Et dangereux, comme vous le dites, si vous utilisez django i18n. Il n'y a aucun avantage de performance à donner un nom sans signification au drapeau, c'est-à-dire que la variable non désirée est toujours créée, donc je la donnerais de manière significative, pour être honnête (et explicite). – thepeer

4

La façon dont le Django ManyToManyField fonctionne est qu'il crée une table supplémentaire. Donc disons que vous avez deux modèles, ModelA et ModelB. Si vous avez ...

ModelA.model_b = models.ManyToManyField(ModelB) 

Qu'est-ce que Django ne fait dans les coulisses est crée une table ... app_modela_modelb avec trois colonnes: id, model_a_id, model_b_id.

Gardez cette idée à l'esprit. En ce qui concerne la sauvegarde de ModelB, Django ne lui attribue pas d'identifiant tant qu'il n'est pas sauvegardé. Techniquement, vous pouvez lui assigner manuellement un identifiant et éviter ce problème. Il semble que vous laissez django gérer ce qui est parfaitement acceptable. Django a un problème avec le M2M. Pourquoi? Si ModelB n'a pas encore d'identifiant, que se passe-t-il dans la colonne model_b_id de la table M2M? L'erreur pour null product_id est plus que probablement une erreur de contrainte nulle sur le champ M2M, pas l'ID d'enregistrement ModelB.

Si vous souhaitez qu'ils "réussissent ensemble" ou "échouent ensemble", il est peut-être temps d'examiner les transactions. Vous, par exemple, enveloppez le tout dans une transaction et effectuez une restauration en cas d'échec partiel. Je n'ai pas fait beaucoup de travail personnellement dans ce domaine, alors j'espère que quelqu'un d'autre sera utile sur ce sujet.

+0

+1 pour la transaction – Davy8

+0

semble être bien documenté http://docs.djangoproject.com/fr/dev/topics/db/transactions/ – michael

+0

J'aurais dû supprimer ce commentaire sur m2m. Le composant est * pas * un champ m2m. C'est juste un 'ForeignKey'. Cela ne répond pas vraiment pourquoi le composant ne peut pas être créé juste avant que l'article soit. La raison pour laquelle je soulève ceci est parce que c'est comme ça que ça marche normalement avec les formulaires. Quand vous faites 'modelForm.save()' il créera tous les autres objets nécessaires au processus, et les sauvegardera tous (sauf si vous avez 'commit = False'). Quoi qu'il en soit, je suppose que les 'transactions 'sont une alternative OK. – mpen

1

L'inclusion d'un extrait de code sur les transactions a peu d'intérêt, car vous devriez lire the Django documentation pour avoir une bonne compréhension.

+0

Vous avez raison, mais je cherchais une solution qui n'implique pas du tout de transactions. La documentation sur les transactions semble assez claire. – mpen

+0

Une raison pour éviter une approche de transaction? –