Introduction
In real Django applications, models rarely exist alone. A blog post has an author. A student belongs to a class. An order contains products. A user may have one profile. These connections between models are called relationships.
Django provides three main types of model relationships:
ForeignKeyOneToOneFieldManyToManyField
Understanding these relationships is essential because they are the foundation of real database design. Once you master them, you can build applications such as blogs, e-commerce stores, school systems, social platforms, and management dashboards much more efficiently.
In this tutorial, you will learn how relationships work in Django, how to create them, how to query related data, and how to choose the correct relationship type for each situation.
What You Will Learn
By the end of this tutorial, you will understand:
- what model relationships are
- how
ForeignKeyworks - how
OneToOneFieldworks - how
ManyToManyFieldworks - how to query related objects
- how reverse relationships work
- how to use
related_name - common mistakes and best practices
Prerequisites
Before starting, you should already know:
- Django models basics
- migrations
- Django admin basics
- QuerySets and ORM basics
1. What Are Relationships in Django?
A relationship connects one model to another.
For example:
- one author can write many posts
- one user has one profile
- one student can join many courses, and one course can have many students
These are different kinds of connections, so Django provides different field types for each one.
2. The Three Main Relationship Types
ForeignKey
Used for one-to-many relationships.
Example:
- one category has many posts
- one author has many books
OneToOneField
Used for one-to-one relationships.
Example:
- one user has one profile
- one employee has one ID card
ManyToManyField
Used for many-to-many relationships.
Example:
- one student has many courses
- one course has many students
3. ForeignKey — One-to-Many Relationship
This is the most common relationship in Django.
Example: Category and Post
- one category can contain many posts
- each post belongs to one category
models.py
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
def __str__(self):
return self.title4. Understanding on_delete
When using ForeignKey, Django requires the on_delete argument.
Example:
category = models.ForeignKey(Category, on_delete=models.CASCADE)This means: if the category is deleted, all related posts are also deleted.
Common on_delete options
models.CASCADE
Delete related objects too.
models.SET_NULL
Set the field to NULL when the related object is deleted.
Requires:
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True)models.PROTECT
Prevent deletion if related objects exist.
models.SET_DEFAULT
Set a default value.
models.DO_NOTHING
Do nothing. Use with caution.
For beginners, CASCADE, SET_NULL, and PROTECT are the most important to know.
5. Creating and Using a ForeignKey
Create objects
category = Category.objects.create(name="Python")
post = Post.objects.create(
title="Django Basics",
content="Learning Django is fun.",
category=category
)Access the related object
post.categoryThis gives the category of the post.
Access a field from the related object
post.category.name6. Reverse Relationship with ForeignKey
Django also lets you go backward.
From a category, you can access all related posts.
category.post_set.all()By default, Django uses:
modelname_setSo from Category, the reverse name for Post becomes:
post_set7. Using related_name
Instead of using post_set, you can define a cleaner reverse name.
Example
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
category = models.ForeignKey(
Category,
on_delete=models.CASCADE,
related_name='posts'
)
def __str__(self):
return self.titleNow you can write:
category.posts.all()This is much more readable than category.post_set.all().
<hr>
8. OneToOneField — One-to-One Relationship
This relationship means one object is connected to exactly one other object.
Common Example: User and Profile
In many Django projects:
- one user has one profile
- one profile belongs to one user
models.py
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(blank=True)
city = models.CharField(max_length=100, blank=True)
def __str__(self):
return self.user.username9. Using a OneToOneField
Create a profile for a user
from django.contrib.auth.models import User
user = User.objects.create(username='alice')
profile = Profile.objects.create(
user=user,
bio='Django developer',
city='Casablanca'
)Access profile from user
user.profileAccess user from profile
profile.userThis is a direct one-to-one connection.
10. Why Use OneToOneField?
Use OneToOneField when:
- each object should match only one other object
- you want to extend the Django
Usermodel with extra information - you want to split data into separate models for clarity
Example:
Instead of placing bio, city, avatar, and phone directly inside the user model, you can place them in a Profile model.
11. ManyToManyField — Many-to-Many Relationship
This relationship is used when both sides can have many related objects.
Example: Student and Course
- one student can take many courses
- one course can have many students
models.py
from django.db import models
class Course(models.Model):
title = models.CharField(max_length=100)
def __str__(self):
return self.title
class Student(models.Model):
name = models.CharField(max_length=100)
courses = models.ManyToManyField(Course)
def __str__(self):
return self.nameDjango automatically creates an intermediate table behind the scenes to manage this relationship.
12. Using a ManyToManyField
Create objects
python_course = Course.objects.create(title="Python")
django_course = Course.objects.create(title="Django")
student = Student.objects.create(name="Youssef")Add courses to a student
student.courses.add(python_course, django_course)Get all courses of a student
student.courses.all()Remove a course
student.courses.remove(python_course)Clear all courses
student.courses.clear()13. Reverse Relationship with ManyToManyField
From a course, you can access all students:
django_course.student_set.all()If you want a cleaner name, use related_name.
Example
class Student(models.Model):
name = models.CharField(max_length=100)
courses = models.ManyToManyField(Course, related_name='students')
def __str__(self):
return self.nameNow you can write:
django_course.students.all()14. Relationship Comparison
ForeignKey
One object belongs to one parent, but the parent can have many children.
Example:
- many posts → one category
OneToOneField
One object matches exactly one other object.
Example:
- one user ↔ one profile
ManyToManyField
Both sides can have many related objects.
Example:
- many students ↔ many courses
15. Real Blog Example
Let us build a more realistic blog structure.
- a post belongs to one author
- a post belongs to one category
- a post can have many tags
models.py
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Tag(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='posts')
tags = models.ManyToManyField(Tag, related_name='posts')
title = models.CharField(max_length=200)
content = models.TextField()
def __str__(self):
return self.title16. Querying Related Data
Get all posts of a category
category.posts.all()Get all posts written by a user
user.posts.all()Get all tags of a post
post.tags.all()Get all posts with a given tag
tag.posts.all()Filter posts by category name
Post.objects.filter(category__name="Python")Filter posts by author username
Post.objects.filter(author__username="alice")Filter posts by tag name
Post.objects.filter(tags__name="django")
Notice the use of double underscores __ to go across relationships.
17. Querying Across Relationships
Django ORM lets you move through relationships very easily.
Example
Post.objects.filter(author__profile__city="Casablanca")This means:
- get posts
- whose author
- has a profile
- whose city is Casablanca
This is one of Django ORM’s strongest features.
18. Migrations with Relationships
After creating or changing relationships in models, run:
python manage.py makemigrations
python manage.py migrateDjango will generate the necessary database structure automatically.
For ManyToManyField, Django also creates the intermediate table for you.
19. Using Relationships in the Admin Panel
Django admin handles relationships very well.
If you register the models:
from django.contrib import admin
from .models import Category, Tag, Post, Profile, Course, Student
admin.site.register(Category)
admin.site.register(Tag)
admin.site.register(Post)
admin.site.register(Profile)
admin.site.register(Course)
admin.site.register(Student)then in the admin panel you can:
- select categories from dropdowns
- assign tags with multi-select widgets
- link profiles to users
- connect students to courses
This makes relationships much easier to manage visually.
20. Common Beginner Mistakes
Forgetting on_delete
A ForeignKey or OneToOneField requires on_delete.
Confusing ForeignKey and ManyToManyField
If one object can have many related objects on both sides, use ManyToManyField, not ForeignKey.
Forgetting null=True when using SET_NULL
This will cause errors.
Wrong:
category = models.ForeignKey(Category, on_delete=models.SET_NULL)Correct:
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True)Accessing reverse relations with the wrong name
Without related_name, Django uses names like post_set.
Adding many-to-many relations before saving the object
You usually need to save the object first before using .add().
Example:
student = Student(name="Amine")
student.save()
student.courses.add(course)21. Best Practices
- use
related_namefor cleaner reverse queries - choose relationship types carefully
- use
OneToOneFieldto extend user data - use
ManyToManyFieldonly when both sides can truly have many objects - keep model design simple and logical
- name relationships clearly
22. Mini Project Example
Library System
A library system may have:
- one author writes many books
- one book belongs to one category
- one book can have many readers
Example
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Category(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Reader(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='books')
readers = models.ManyToManyField(Reader, related_name='books')
def __str__(self):
return self.titleThis small example combines all three major relationship types except OneToOneField.
23. Summary
In this tutorial, you learned that:
- Django relationships connect models together
ForeignKeyis for one-to-manyOneToOneFieldis for one-to-oneManyToManyFieldis for many-to-manyrelated_nameimproves reverse access- Django ORM allows easy querying across relationships
- good model design is essential in real Django applications
Once you understand relationships, you can design much more powerful and realistic databases.
24. Mini Quiz
1. Which field creates a one-to-many relationship?
A. OneToOneField
B. ManyToManyField
C. ForeignKey
D. CharField
2. Which field is best for extending the User model with profile information?
A. ForeignKey
B. OneToOneField
C. ManyToManyField
D. BooleanField
3. Which field is used when both models can have many related objects?
A. ForeignKey
B. OneToOneField
C. ManyToManyField
D. TextField
4. What does related_name do?
A. Deletes related objects
B. Renames a model
C. Defines a reverse access name
D. Creates a template
5. Which option deletes child objects when the parent is deleted?
A. SET_NULL
B. PROTECT
C. CASCADE
D. SET_DEFAULT
25. What Comes Next?
The next ideal tutorial is:
Tutorial 25: Django Model Methods and Properties
Subject: adding business logic inside models using methods, __str__, custom methods, and properties.
That tutorial will show you how to make your models smarter and more useful.
💬 Comments
No comments yet. Be the first to comment!
Login to comment.