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:
ForeignKeyOneToOneFieldManyToManyFieldUnderstanding 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.
By the end of this tutorial, you will understand:
ForeignKey worksOneToOneField worksManyToManyField worksrelated_nameBefore starting, you should already know:
A relationship connects one model to another.
For example:
These are different kinds of connections, so Django provides different field types for each one.
ForeignKeyUsed for one-to-many relationships.
Example:
OneToOneFieldUsed for one-to-one relationships.
Example:
ManyToManyFieldUsed for many-to-many relationships.
Example:
ForeignKey — One-to-Many RelationshipThis is the most common relationship in Django.
models.pyfrom 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.titleon_deleteWhen 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.
on_delete optionsmodels.CASCADEDelete related objects too.
models.SET_NULLSet 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.PROTECTPrevent deletion if related objects exist.
models.SET_DEFAULTSet a default value.
models.DO_NOTHINGDo nothing. Use with caution.
For beginners, CASCADE, SET_NULL, and PROTECT are the most important to know.
ForeignKeycategory = Category.objects.create(name="Python")
post = Post.objects.create(
title="Django Basics",
content="Learning Django is fun.",
category=category
)post.categoryThis gives the category of the post.
post.category.nameForeignKeyDjango 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_setrelated_nameInstead of using post_set, you can define a cleaner reverse name.
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>
OneToOneField — One-to-One RelationshipThis relationship means one object is connected to exactly one other object.
In many Django projects:
models.pyfrom 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.usernameOneToOneFieldfrom django.contrib.auth.models import User
user = User.objects.create(username='alice')
profile = Profile.objects.create(
user=user,
bio='Django developer',
city='Casablanca'
)user.profileprofile.userThis is a direct one-to-one connection.
OneToOneField?Use OneToOneField when:
User model with extra informationExample:
Instead of placing bio, city, avatar, and phone directly inside the user model, you can place them in a Profile model.
ManyToManyField — Many-to-Many RelationshipThis relationship is used when both sides can have many related objects.
models.pyfrom 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.
ManyToManyFieldpython_course = Course.objects.create(title="Python")
django_course = Course.objects.create(title="Django")
student = Student.objects.create(name="Youssef")student.courses.add(python_course, django_course)student.courses.all()student.courses.remove(python_course)student.courses.clear()ManyToManyFieldFrom a course, you can access all students:
django_course.student_set.all()If you want a cleaner name, use related_name.
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()ForeignKeyOne object belongs to one parent, but the parent can have many children.
Example:
OneToOneFieldOne object matches exactly one other object.
Example:
ManyToManyFieldBoth sides can have many related objects.
Example:
Let us build a more realistic blog structure.
models.pyfrom 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.titlecategory.posts.all()user.posts.all()post.tags.all()tag.posts.all()Post.objects.filter(category__name="Python")Post.objects.filter(author__username="alice")Post.objects.filter(tags__name="django")
Notice the use of double underscores __ to go across relationships.
Django ORM lets you move through relationships very easily.
Post.objects.filter(author__profile__city="Casablanca")This means:
This is one of Django ORM’s strongest features.
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.
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:
This makes relationships much easier to manage visually.
on_deleteA ForeignKey or OneToOneField requires on_delete.
ForeignKey and ManyToManyFieldIf one object can have many related objects on both sides, use ManyToManyField, not ForeignKey.
null=True when using SET_NULLThis 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)Without related_name, Django uses names like post_set.
You usually need to save the object first before using .add().
Example:
student = Student(name="Amine")
student.save()
student.courses.add(course)related_name for cleaner reverse queriesOneToOneField to extend user dataManyToManyField only when both sides can truly have many objectsA library system may have:
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.
In this tutorial, you learned that:
ForeignKey is for one-to-manyOneToOneField is for one-to-oneManyToManyField is for many-to-manyrelated_name improves reverse accessOnce you understand relationships, you can design much more powerful and realistic databases.
A. OneToOneField
B. ManyToManyField
C. ForeignKey
D. CharField
User model with profile information?A. ForeignKey
B. OneToOneField
C. ManyToManyField
D. BooleanField
A. ForeignKey
B. OneToOneField
C. ManyToManyField
D. TextField
related_name do?A. Deletes related objects
B. Renames a model
C. Defines a reverse access name
D. Creates a template
A. SET_NULL
B. PROTECT
C. CASCADE
D. SET_DEFAULT
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.