OUJOOD.COM
Pourquoi les relations entre modèles
Une base de données relationnelle est conçue pour éviter la duplication : plutôt que de répéter les informations d'un auteur dans chaque article qu'il écrit, on stocke l'auteur une seule fois et on établit un lien entre les deux tables. Django modélise ces liens directement dans les classes Python avec trois types de champs de relation.
ForeignKey — relation plusieurs-à-un
C'est la relation la plus courante. Un article appartient à un auteur, mais un auteur peut écrire plusieurs articles. La clé étrangère se place du côté "plusieurs" — ici, dans le modèle Article :
from django.db import models
class Auteur(models.Model):
nom = models.CharField(max_length=255)
email = models.EmailField()
def __str__(self):
return self.nom
class Article(models.Model):
titre = models.CharField(max_length=255)
contenu = models.TextField()
# Un article appartient à un auteur
# Si l'auteur est supprimé, ses articles le sont aussi (CASCADE)
auteur = models.ForeignKey(Auteur, on_delete=models.CASCADE)
def __str__(self):
return self.titre
Le paramètre on_delete est obligatoire. Il définit ce qui se passe quand l'objet référencé est supprimé :
CASCADE — supprime aussi les objets liés. L'option la plus courante.
SET_NULL — met la clé étrangère à NULL (nécessite null=True).
PROTECT — empêche la suppression si des objets liés existent.
SET_DEFAULT — assigne la valeur par défaut définie sur le champ.
Interroger une ForeignKey
# Accéder à l'auteur d'un article article = Article.objects.get(id=1) print(article.auteur.nom) # Tous les articles d'un auteur (relation inverse) auteur = Auteur.objects.get(id=1) articles = auteur.article_set.all() # Filtrer les articles par attribut de l'auteur (double underscore) Article.objects.filter(auteur__nom='Dubois')
Django crée automatiquement une relation inverse : depuis un Auteur, vous accédez à ses articles via auteur.article_set.all(). Le nom article_set est généré automatiquement — vous pouvez le changer avec related_name :
# related_name personnalise le nom de la relation inverse auteur = models.ForeignKey(Auteur, on_delete=models.CASCADE, related_name='articles') # Maintenant on peut écrire : auteur.articles.all() # Plus lisible que auteur.article_set.all()
ManyToManyField — relation plusieurs-à-plusieurs
Un article peut avoir plusieurs tags, et un tag peut apparaître dans plusieurs articles. Django gère cette relation avec ManyToManyField et crée automatiquement une table intermédiaire en base de données.
class Tag(models.Model):
nom = models.CharField(max_length=100)
def __str__(self):
return self.nom
class Article(models.Model):
titre = models.CharField(max_length=255)
auteur = models.ForeignKey(Auteur, on_delete=models.CASCADE)
# Un article peut avoir plusieurs tags, un tag plusieurs articles
tags = models.ManyToManyField(Tag, blank=True)
Interroger une ManyToMany
# Ajouter des tags à un article article = Article.objects.get(id=1) tag_python = Tag.objects.get(nom='Python') article.tags.add(tag_python) # Tous les tags d'un article article.tags.all() # Tous les articles ayant le tag 'Python' Article.objects.filter(tags__nom='Python') # Retirer un tag article.tags.remove(tag_python)
OneToOneField — relation un-à-un
Chaque utilisateur a exactement un profil, et chaque profil appartient à exactement un utilisateur. OneToOneField est une ForeignKey avec une contrainte d'unicité supplémentaire.
from django.contrib.auth.models import User
class Profil(models.Model):
# Chaque profil est lié à exactement un utilisateur Django
utilisateur = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(blank=True)
avatar = models.ImageField(upload_to='avatars/', blank=True)
def __str__(self):
return f"Profil de {self.utilisateur.username}"
# Accéder au profil depuis l'utilisateur user = User.objects.get(username='admin') print(user.profil.bio) # Accéder à l'utilisateur depuis le profil profil = Profil.objects.get(id=1) print(profil.utilisateur.email)
Optimiser les requêtes avec select_related
Par défaut, Django effectue une requête SQL séparée chaque fois que vous accédez à un objet lié. Sur une liste de 100 articles, accéder à article.auteur.nom génère 101 requêtes. select_related() charge les relations en une seule requête SQL avec un JOIN :
# Sans select_related : 1 + N requêtes SQL articles = Article.objects.all() for a in articles: print(a.auteur.nom) # 1 requête par auteur ! # Avec select_related : 1 seule requête SQL (JOIN) articles = Article.objects.select_related('auteur').all() for a in articles: print(a.auteur.nom) # Données déjà chargées
Pour les relations ManyToMany, utilisez prefetch_related() à la place — il effectue deux requêtes optimisées plutôt qu'un JOIN qui peut dupliquer les lignes.
Par carabde | Mis à jour le 05 mai 2025