HOWTO · Django

Recherches inversées de clés étrangères dans Django

En savoir plus sur les recherches inversées de clés étrangères dans Django

Sur cette page

Pour trouver les lignes qui font référence à une instance donnée d’un modèle Django, remontez la relation ForeignKey depuis cette instance. Django expose cette relation sous forme de gestionnaire : appelez des méthodes comme all() ou filter() dessus pour obtenir un QuerySet. L’option related_name donne un nom lisible à ce gestionnaire inverse.

Fonctionnement des recherches inversées de clés étrangères dans Django

Une ForeignKey est déclarée sur le modèle qui contient la référence. Par exemple, chaque ligne Student peut faire référence à une ligne Teacher. En partant d’une instance Teacher, la recherche inverse renvoie les lignes Student dont la ForeignKey pointe vers cet enseignant. C’est utile lorsqu’une page ou une tâche part d’un enregistrement parent et doit accéder aux enregistrements qui lui sont associés.

Ici, un élève peut avoir un professeur principal et un professeur préféré. Les deux champs pointent vers Teacher; chacun doit donc avoir un related_name distinct. Ces noms s’utilisent depuis une instance Teacher :

Dans un modèle Django, définissez Teacher avec les champs name et subject, puis attribuez à Student deux champs ForeignKey facultatifs vers Teacher : class_teacher avec related_name="class_students", et favorite_teacher avec related_name="favorite_students". Ces noms sont volontairement différents : teacher.class_students suit class_teacher, tandis que teacher.favorite_students suit favorite_teacher. Si vous omettez related_name, Django dérive généralement un accesseur inverse à partir du nom du modèle; deux ForeignKey de Student vers Teacher ne peuvent pas utiliser le même accesseur inverse. Définir explicitement les deux noms rend les chemins plus clairs. Ces déclarations de modèle sont propres à Django et doivent se trouver dans un projet Django configuré.

Récupérer tous les objets associés

En supposant qu’un Teacher de clé primaire 1 existe, récupérez les élèves qui désignent cet enseignant comme professeur principal avec Teacher.objects.get(pk=1), puis teacher.class_students.all().

La valeur de students est un QuerySet, qui représente les lignes correspondantes et peut être parcouru. La seconde relation fonctionne de la même manière : utilisez teacher.favorite_students.all() pour obtenir les élèves qui indiquent que cet enseignant est leur professeur préféré. Le nom du gestionnaire vient du related_name de la ForeignKey, et non du modèle Teacher. La recherche avec get() suppose qu’une ligne Teacher portant cette clé primaire existe.

Pour illustrer en mémoire ce même sens de recherche, partez d’un enregistrement d’enseignant et sélectionnez les enregistrements d’élèves qui font référence à son identifiant :

Le code et les libellés ou sorties affichés dans cette section restent en anglais.

teacher_id = 1
students = [
    {"name": "Asha", "class_teacher_id": 1},
    {"name": "Ben", "class_teacher_id": 2},
]
related_students = [
    student for student in students
    if student["class_teacher_id"] == teacher_id
]
print([student["name"] for student in related_students])
['Asha']

Cette illustration en Python standard montre le sens de la relation; un QuerySet Django effectue la recherche correspondante dans la base de données.

Filtrer les objets associés à un enregistrement précis

Utilisez filter() sur le gestionnaire inverse lorsque vous devez aussi appliquer une condition au modèle associé. Par exemple, pour trouver l’élève dont la clé primaire est 5 parmi les élèves de classe de l’enseignant 25, appelez Teacher.objects.get(pk=25), puis teacher.class_students.filter(pk=5).

filter() renvoie un QuerySet et non une instance unique de Student. Dans cet exemple, il contient au plus une ligne, car les clés primaires sont uniques; il peut toutefois n’en contenir aucune si cet élève n’est pas associé à cet enseignant. Parcourez le résultat ou vérifiez s’il est non vide lorsque vous devez agir sur les correspondances.

Gérer les résultats vides et les pièges courants

Les méthodes all() et filter() d’un gestionnaire inverse renvoient un QuerySet vide lorsqu’aucune ligne Student ne correspond; elles ne renvoient pas un objet Student manquant. Vous pouvez donc parcourir le résultat sans risque, même en l’absence de correspondance. Utilisez get() uniquement pour la recherche initiale de Teacher lorsque vous vous attendez à ce que cet enseignant existe; contrairement à filter(), get() lève Teacher.DoesNotExist si ce n’est pas le cas.

Le modèle ci-dessus autorise l’absence de valeur pour l’une ou l’autre ForeignKey. Un Student dont class_teacher est nul n’apparaît dans le résultat inverse class_students d’aucun enseignant; de même, un favorite_teacher nul est absent de favorite_students. Si votre application exige que chaque élève ait un enseignant, supprimez null=True et prévoyez la migration de base de données nécessaire. Lorsqu’un modèle comporte plusieurs ForeignKey vers la même cible, gardez des noms inverses distincts.