Hello! Welcome back to our journey through the Django framework.
In our last lesson, we laid the foundation for our application's data layer by learning how to define Django models and their basic fields. You now know how to create a model that acts as a blueprint for a database table, using fields like CharField, IntegerField, and DateTimeField to define its structure.
Today, we'll build on that foundation to explore one of the most powerful features of a relational database: relationships. An application's data is rarely isolated; it's interconnected. This lesson will teach you how to create those connections within Django.
Your familiarity with SQL means you already understand concepts like foreign keys and join tables. Today's goal is to see how Django's ORM elegantly abstracts these database-level concepts into simple, Pythonic field types. By the end of this lesson, you will be able to establish relationships between your models using ForeignKey, OneToOneField, and ManyToManyField.
The Three Types of Model Relationships
In Django, as in most relational database systems, there are three primary ways to relate models to each other:
- One-to-Many: One object is related to many other objects. For example, one
Authorcan write manyBlog Posts, but eachBlog Posthas only oneAuthor. - One-to-One: One object is related to exactly one other object. For example, a
UserProfilemight extend the built-inUsermodel, with each user having one and only one profile. - Many-to-Many: An object can be related to many other objects, and vice-versa. For example, a
Blog Postcan have manyTags, and aTagcan be applied to manyBlog Posts.
This image provides a great visual summary of how these concepts map to Django's fields.

To get a solid conceptual understanding of how these relationships work at a database level, let's watch the beginning of the following video.
Database Relationships | One To Many & Many to Many | Django (3.0) Crash Course Tutorials (pt 6)
The video 'Database Relationships' by Dennis Ivy offers clear, diagram-based explanations of one-to-many and many-to-many relationships before diving into the Django implementation.
Watch from 00:27 to 04:49. Pay close attention to: How a one-to-many relationship uses a foreign key column. Why a many-to-many relationship requires a third, intermediary table (also known as a join table or through table).
Now that you have the conceptual framework, let's see how to implement each of these in Django.
One-to-Many Relationships: ForeignKey
A one-to-many relationship is the most common type. You define it using models.ForeignKey.
You place the ForeignKey field on the "many" side of the relationship. In our Author and Post example, a post has one author, so the ForeignKey goes in the Post model.
# models.py
from django.db import models
class Manufacturer(models.Model):
name = models.CharField(max_length=100)
# ... other fields
def __str__(self):
return self.name
class Car(models.Model):
name = models.CharField(max_length=100)
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
def __str__(self):
return self.name
The on_delete Argument
The on_delete argument is required for ForeignKey and is critical for database integrity. It tells Django what to do when the object on the "one" side of the relationship is deleted.
models.CASCADE: This is the most common behavior. If you delete aManufacturer, allCarobjects associated with it will also be deleted.models.PROTECT: Prevents the deletion of the referenced object. If you try to delete aManufacturerthat still hasCars associated with it, Django will raise aProtectedError.models.SET_NULL: Sets theForeignKeytoNULL. This requires the field to havenull=Trueset. For example, if you delete aManufacturer, themanufacturerfield on its cars becomesNULL.models.SET_DEFAULT: Sets theForeignKeyto its default value. This requires adefaultvalue to be set for the field.models.RESTRICT: Similar toPROTECT, but the specific error and timing can depend on the database backend.PROTECTis generally preferred.
For a detailed walkthrough of implementing and querying ForeignKey relationships, the following video is excellent.
Understanding Django Model Relationships
The 'Understanding Django Model Relationships' video by Pretty Printed provides a thorough, code-centric explanation of ForeignKey.
Watch the first part of the video (00:28 - 12:59). The presenter does a great job of showing: How to define the ForeignKey. What happens in the database (the _id column is created). How to create related objects. How to query across relationships using the double-underscore (__) syntax, a powerful feature of the ORM.
Many-to-Many Relationships: ManyToManyField
To define a many-to-many relationship, use models.ManyToManyField. As you saw in the conceptual video, Django handles the creation of the intermediary table for you automatically.
You can place the ManyToManyField on either of the two related models, but not both. By convention, you place it on the object that will be edited in forms. For example, when creating a Post, you would select its Tags, so we put the field on the Post model.
# models.py
from django.db import models
class Topping(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Pizza(models.Model):
name = models.CharField(max_length=50)
toppings = models.ManyToManyField(Topping, blank=True) # blank=True makes it optional in forms
def __str__(self):
return self.name
The next section of the Pretty Printed video continues with a great explanation of ManyToManyField.
Understanding Django Model Relationships
Continuing with the same video, we now move to many-to-many relationships.
Watch from 12:59 to 25:57. Focus on: How Django automatically creates a third table for the relationship. The .add(), .create(), and .all() methods for managing related objects. The similarity in query syntax (using __) to ForeignKey.
Advanced: The through Argument
Sometimes you need to store extra information about a many-to-many relationship. For example, in a relationship between a Musician and a Group, you might want to store the date the musician joined the group. In this case, you can create a custom intermediary model yourself and link it using the through argument on the ManyToManyField. This is an advanced technique, but it's good to know it exists. You can read more about it in the Django documentation on extra fields on many-to-many relationships.
One-to-One Relationships: OneToOneField
A one-to-one relationship is defined with models.OneToOneField. It's essentially a ForeignKey with a unique=True constraint, ensuring that each object can only be related once.
The most common use case is to extend an existing model, particularly Django's built-in User model, to create a user "profile" with additional information.
# models.py
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
# This creates a one-to-one link to Django's User model
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(blank=True)
location = models.CharField(max_length=100, blank=True)
birth_date = models.DateField(null=True, blank=True)
def __str__(self):
return self.user.username
Here, every User can have exactly one Profile, and every Profile is associated with exactly one User. Just like ForeignKey, OneToOneField also requires the on_delete argument.
For a definitive textual reference on all three relationship types, the official Django documentation is the best source.
The official Django documentation provides concise and accurate definitions for all relationship fields. It's a great resource to solidify your understanding.
Skim through the sections on 'Many-to-one relationships', 'Many-to-many relationships', and 'One-to-one relationships'. You don't need to read every detail, but notice the examples and the structure. This will serve as a good reference.
Test your understanding!
You are designing models for a simple course enrollment system. You need three models: Student, Course, and Enrollment.
- A
Studentcan enroll in manyCourses. - A
Coursecan have manyStudents. - The
Enrollmentmodel should track whichStudentis in whichCourse, and also store theenrollment_date.
How would you structure these models and their relationships? Think about which relationship fields you would use and where you would place them.
Show answer
This is a classic case for a many-to-many relationship with extra data, which requires a custom through model.
from django.db import models
class Student(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
def __str__(self):
return f'{self.first_name} {self.last_name}'
class Course(models.Model):
name = models.CharField(max_length=200)
# We define the ManyToManyField here, and tell it to use
# the Enrollment model as the intermediary table.
students = models.ManyToManyField(Student, through='Enrollment')
def __str__(self):
return self.name
class Enrollment(models.Model):
# This is the intermediary model.
student = models.ForeignKey(Student, on_delete=models.CASCADE)
course = models.ForeignKey(Course, on_delete=models.CASCADE)
enrollment_date = models.DateField(auto_now_add=True)
class Meta:
# Ensures a student can't enroll in the same course twice
unique_together = ('student', 'course')
- The
Coursemodel has aManyToManyFieldpointing toStudent. - We specify
through='Enrollment'to tell Django that we are using a custom model to manage the relationship. - The
Enrollmentmodel itself contains aForeignKeytoStudent, aForeignKeytoCourse, and the extra data field (enrollment_date).
Conclusion
In this lesson, we bridged the gap between individual data tables and a fully relational database schema. You've learned how to use Django's ORM to define the connections that give your application's data structure and meaning.
Key Takeaways:
- One-to-Many (
ForeignKey): Used when one object can be related to many others. It's placed on the "many" side of the relationship and requires anon_deletepolicy. - Many-to-Many (
ManyToManyField): Used when objects on both sides can be related to multiple objects on the other side. Django automatically creates the necessary join table. - One-to-One (
OneToOneField): A restrictive version ofForeignKeythat ensures an object is linked to only one other object. It's perfect for extending existing models, like creating user profiles. - The ORM's double-underscore syntax (
__) allows you to intuitively query across these relationships.
You now have the complete toolkit to design a robust data model for a complex application. But defining models is only the first step. In our next lesson, we will learn how to translate these model definitions into an actual database schema using migrations, with the makemigrations and migrate commands.