Django crée automatiquement des outils pour naviguer dans les relations entre vos modèles, y compris les **relations inverses**. Ces outils sont appelés **gestionnaires de relations (RelatedManager)**. Pour les champs ManyToManyField, comprendre et maîtriser le gestionnaire par défaut (_set) et sa personnalisation (related_name) est fondamental.
Rappel : Un ManyToManyField ne doit être défini que sur l'un des deux modèles pour établir une relation bidirectionnelle (comme l'indique cet excellent lien sur StackOverflow : Différence entre les champs de relation).
1. Les Modèles de Base
Considérons les deux modèles suivants qui définissent une relation Plusieurs-à-Plusieurs (Many-to-Many) où un utilisateur peut appartenir à plusieurs entreprises et vice-versa :
class User(models.Model):
username = models.CharField(max_length=100, unique=True)
companies = models.ManyToManyField('Company', blank=True)
class Company(models.Model):
name = models.CharField(max_length=255)
Accès Direct (Naturel)
Pour obtenir toutes les entreprises d'un utilisateur, on utilise le nom du champ :
user_instance.companies.all()
2. Le Gestionnaire d'Accès Inverse par Défaut (`_set`)
Puisque le modèle Company n'a pas de champ explicite pointant vers User, Django crée automatiquement un gestionnaire d'accès inverse. Ce gestionnaire est nommé en utilisant la syntaxe : <modèle_source_en_minuscule>_set.
Dans notre cas, le modèle source de la relation inverse est **`User`**. Le gestionnaire s'appelle donc user_set :
# Accès inverse par défaut : Obtenir tous les utilisateurs d'une entreprise
company_instance.user_set.all()
# Retourne un QuerySet d’objets User.
Ce mécanisme est la manière dont Django gère par défaut l'accès depuis l'objet qui ne porte pas la définition du champ relationnel.
3. La Méthode Recommandée : Utiliser `related_name`
Le nom par défaut _set est souvent peu clair. Il est fortement recommandé d'utiliser l'argument **related_name** lors de la définition du champ ManyToManyField pour spécifier un nom de gestionnaire inverse plus explicite.
Modification du Modèle
Nous remplaçons user_set par le nom plus logique users :
class User(models.Model):
username = models.CharField(max_length=100, unique=True)
# Définition du nom de la relation inverse comme 'users'
companies = models.ManyToManyField('Company', blank=True, related_name='users')
class Company(models.Model):
name = models.CharField(max_length=255)
Utilisation du Nom Personnalisé
L'accès inverse devient alors plus intuitif :
# Accès inverse personnalisé : Obtenir tous les utilisateurs d'une entreprise
company_instance.users.all()
L'utilisation de related_name rend votre code plus lisible et évite les conflits potentiels si plusieurs relations pointent vers le même modèle.