Pour définit une relation ManyToMany dans votre modèle, vous pouvez utiliser le champ models.ManyToManyField
ManyToMany c'est quoi au juste?
Many-to-many signifique littéralement " Plusieurs à Plusieurs ". C'est un concept qui permet de lier plusieurs items à plusieurs endroits. C'est une sorte de clé étrangère qui peut contenir plusieurs items.
Exemple de many to many
Passons à la pratique en gardant notre exemple d'eboutique:
backoffice/models.py
# coding: utf-8 from django.db import models class Product(models.Model): """ Produit : prix, code, etc. """ class Meta: verbose_name = "Produit" name = models.CharField(max_length=100) code = models.CharField(max_length=10, null=True, blank=True, unique=True) price_ht = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="Prix unitaire HT") price_ttc = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="Prix unitaire TTC") def __unicode__(self): return u"{0} [{1}]".format(self.name, self.code) class ProductItem(models.Model): """ Déclinaison de produit déterminée par des attributs comme la couleur, etc. """ class Meta: verbose_name = "Déclinaison Produit" product = models.ForeignKey('Product') code = models.CharField(max_length=10, null=True, blank=True, unique=True) code_ean13 = models.CharField(max_length=13) attributes = models.ManyToManyField("ProductAttributeValue", related_name="product_item", null=True, blank=True) def __unicode__(self): return u"{0} [{1}]".format(self.product.name, self.code) class ProductAttribute(models.Model): """ Attributs produit """ class Meta: verbose_name = "Attribut" name = models.CharField(max_length=100) def __unicode__(self): return self.name class ProductAttributeValue(models.Model): """ Valeurs des attributs """ class Meta: verbose_name = "Valeur attribut" ordering = ['position'] value = models.CharField(max_length=100) product_attribute = models.ForeignKey('ProductAttribute', verbose_name="Unité") position = models.PositiveSmallIntegerField("Position", null=True, blank=True) def __unicode__(self): return u"{0} [{1}]".format(self.value, self.product_attribute)
Indiquons à Django que nous souhaitons voir ces modèles dans l'adminsitration:
backoffice/admin.py
from django.contrib import admin from models import * admin.site.register(Product) admin.site.register(ProductItem) admin.site.register(ProductAttribute) admin.site.register(ProductAttributeValue)
Créons nos éléments:
test.py
# coding: utf-8 import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "eboutique.settings") from django.db import models from backoffice.models import * # Création d'un attribut product_attribute = ProductAttribute(name="couleur") product_attribute.save() # Création des valeurs des attributs attribute1 = ProductAttributeValue(value="bleu", product_attribute=product_attribute, position=0) attribute1.save() attribute2 = ProductAttributeValue(value="jaune", product_attribute=product_attribute, position=0) attribute2.save() attribute2 = ProductAttributeValue(value="brun", product_attribute=product_attribute, position=0) attribute2.save() # Création du produit product = Product(name="Tshirt", code="54065", price_ht=25, price_ttc=30) product.save() # Création d'une déclinaison de produit product_item = ProductItem(product=product, code="5046", code_ean13="a1") product_item.save() product_item.attributes.add(attribute1) product_item.attributes.add(attribute1) product_item.save()
Rendez-vous sur la fiche de la déclinaison de produit:
On remarque qu'il est possible de sélectionner plusieurs valeurs dans un même champ (le champ attributes ): c'est cela un manytomany
Many to many queryset
Dans notre fichier test.py (voir plus haut) nous avons vu comment ajouter en python une valeur dans cette relation many-to-many :
product_item.attributes.add(attribute1)
Il faudra cependant vous assurez que l'item product_item a bien été sauvegardé en base auparavant:
product_item.save() product_item.attributes.add(attribute1)
Pour voir tous les attributs associés:
product_item.attributes.all()
D'autres exemples d'utilisation:
i1 = ProductItem.objects.get(pk=1) # Tshirt [5046] a1 = ProductAttributeValue.objects.get(pk=1) # bleu [couleur] ProductAttributeValue.objects.all() # [<ProductAttributeValue: bleu [couleur]>, <ProductAttributeValue: jaune [couleur]>, <ProductAttributeValue: brun [couleur]>] a1.product_item.all() # [<ProductItem: Tshirt [5046]>] ProductAttributeValue.objects.filter(product_item=i1) # [<ProductAttributeValue: bleu [couleur]>, <ProductAttributeValue: brun [couleur]>] ProductAttributeValue.objects.filter(product_item__id=1) # [<ProductAttributeValue: bleu [couleur]>, <ProductAttributeValue: brun [couleur]>]
On remarque qu'il est possible de créer des queryset quelque soit l'item de départ (ProductAttributeValue ou ProductItem). Le reverse m2m nécessite cependant une information: related_name que nous avons renseigné dans l'exemple plus haut par la valeur product_item
Supprimer une relation many-to-many
Vous pouvez supprimer un item avec la méthode remove
:
i1.attributes.remove(a1)
Ou dans l'autre sens:
a1.product_item.remove(i1)
Si vous voulez vider tous les attributs:
a1.product_item.clear()
Cette syntaxe fonctionne aussi:
a1.product_item = []