How to Reverse Lookups of Foreign Keys in Django
Django is an efficient web development framework that simplifies the creation of web applications. Django makes it effortless to deal with authenticating and authorizing, creating HTML templates, dealing with static files, interacting with databases, and performing CRUD operations over them.
Speaking of databases, Django simplifies most of the queries you can make while working with databases. One such query is the reverse look; an example is when we have to get all the objects of a table that are referencing a particular record of either the same table or a different model.
This article will show how simple it is to perform reverse lookups of foreign keys in Django.
Reverse Lookups in Django Models
Before we move onto the actual step, we need some models or tables for demonstration. We will consider two entities: teacher and student. The student has two types of teachers; a class teacher and a favorite teacher. The Student
model is referencing the Teacher
model.
from django.db import models
class Teacher(models.Model):
name = models.CharField(max_length=200)
subject = models.CharField(max_length=200)
class Student(models.Model):
name = models.CharField(max_length=200)
classTeacher = models.ForeignKey(
Teacher, on_delete=models.SET_NULL, null=True, related_name="classTeacherOf"
)
favouriteTeacher = models.ForeignKey(
Teacher, on_delete=models.SET_NULL, null=True, related_name="favouriteTeacherOf"
)
Two fields of the Student
model are referencing the Teacher
model. In Django, when referencing the same model more than once, we have to provide a related_name
for all the fields because Django’s default related_name
for a single referencing field clashes with other referencing fields. Otherwise, Django will throw an exception.
The related_name
is what we use for the reverse lookup. In general, it is a good practice to provide a related_name
for all the foreign keys rather than using Django’s default-related name.
Example 1
We have a teacher whose id
is 1
. If we have to get all the students who have this individual as their class teacher, we will do the following:
teacher = Teacher.objects.get(id=1)
students = teacher.classTeacherOf.all()
print(students) # A QuerySet of Student objects
Notice how we are using the related_name
. The teacher.classTeacherOf
is a manager object which means that we call methods like all()
, filter()
, exclude()
on it.
Example 2
We have a teacher whose id
is 6
. If we have to get all the students who regard this teacher as their favorite teacher, we will do something like this:
teacher = Teacher.objects.get(id=6)
students = teacher.favouriteTeacherOf.all()
print(students) # A QuerySet of Student objects
Example 3
We have a teacher whose id
is 25
. If we have to check whether this teacher is the class teacher of a student whose id
is 5
, we will do something as follows:
teacher = Teacher.objects.get(id=25)
student = teacher.classTeacherOf.filter(id=5)
print(student) # A QuerySet of either 1 or 0 Student
Note that if no objects are found in the reverse lookup, an empty QuerySet
is returned.