Paramclasses Academy - Blog

Tutorials Details

Django Relationships: ForeignKey, OneToOneField, ManyToManyField, and Related Data — Complete Practical Guide

Django Relationships: ForeignKey, OneToOneField, ManyToManyField, and Related Data — Complete Practical Guide

October 19, 2026

Django Relationships: ForeignKey, OneToOneField, ManyToManyField, and Related Data

Introduction

In real-world Django applications, data is connected. A student may belong to one classroom, a user may have one profile, and a course may contain many students.

Django provides three important model relationship fields:

  1. ForeignKey — represents a many-to-one relationship.

  2. OneToOneField — represents a one-to-one relationship.

  3. ManyToManyField — represents a many-to-many relationship.

In this module, you will learn how to create these relationships, run migrations, retrieve related data, and display it in Django templates.

1. Understanding ForeignKey

A ForeignKey connects multiple records in one model to a single record in another model.

For example:

  • One classroom can contain many students.

  • Each student belongs to one classroom.

Step 1: Create the Classroom Model

Open your app's models.py file.

from django.db import models


class Classroom(models.Model):
    name = models.CharField(max_length=100)
    room_number = models.CharField(max_length=20)

    def __str__(self):
        return self.name

Step 2: Create the Student Model

class Student(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField(unique=True)

    classroom = models.ForeignKey(
        Classroom,
        on_delete=models.CASCADE,
        related_name="students"
    )

    def __str__(self):
        return self.name

The classroom field connects each student to a classroom.

Important options:

  • on_delete=models.CASCADE: deletes related students when their classroom is deleted.

  • related_name="students": allows you to access students through classroom.students.

Choose your deletion behavior carefully. In some applications, you may want to protect a classroom from deletion or allow the relationship to become empty instead.

Step 3: Run Migrations

python manage.py makemigrations
python manage.py migrate

Migrations apply your model changes to the database.

2. Understanding OneToOneField

A OneToOneField connects one record to exactly one related record at a time.

For example, each user can have one profile.

from django.conf import settings
from django.db import models


class UserProfile(models.Model):
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="profile"
    )

    phone = models.CharField(max_length=15, blank=True)
    address = models.TextField(blank=True)

    def __str__(self):
        return f"{self.user} Profile"

This model uses Django's configured user model rather than hardcoding Django's default User class.

You can access the profile from a user:

user.profile

You can also access the associated user from a profile:

profile.user

Important: If a profile has not been created for a user, accessing user.profile can raise a related-object exception. Create the profile when appropriate or handle the missing profile in your application.

3. Understanding ManyToManyField

A ManyToManyField connects multiple records from one model to multiple records in another model.

For example:

  • One student can enroll in multiple courses.

  • One course can contain multiple students.

class Course(models.Model):
    title = models.CharField(max_length=150)
    description = models.TextField(blank=True)

    students = models.ManyToManyField(
        Student,
        related_name="courses",
        blank=True
    )

    def __str__(self):
        return self.title

Django automatically manages an intermediate database table for this relationship.

After creating the model, run:

python manage.py makemigrations
python manage.py migrate

Add Students to a Course

You can use the Django shell:

python manage.py shell

Then execute:

from your_app.models import Student, Course

student = Student.objects.first()
course = Course.objects.create(title="Python Fundamentals")

course.students.add(student)

Replace your_app with the actual name of your Django app. Ensure that at least one student exists before running this example.

To retrieve the enrolled students:

course.students.all()

To find courses for a student:

student.courses.all()

4. Accessing Related Data with Django ORM

Django's ORM makes it easy to retrieve connected records without writing SQL manually for common operations.

Get a Student's Classroom

student = Student.objects.select_related("classroom").get(id=1)

print(student.classroom.name)

select_related() is useful for ForeignKey and OneToOneField relationships because it can retrieve related objects through a SQL join.

Get Students from a Classroom

classroom = Classroom.objects.get(id=1)

students = classroom.students.all()

for student in students:
    print(student.name)

Get Courses for a Student

student = Student.objects.get(id=1)

for course in student.courses.all():
    print(course.title)

Use prefetch_related()

For many-to-many and reverse relationships, prefetch_related() can reduce unnecessary database queries.

courses = Course.objects.prefetch_related("students")

for course in courses:
    print(course.title)

    for student in course.students.all():
        print(student.name)

Use select_related() and prefetch_related() when appropriate, especially when displaying lists of related data.

5. Register Related Models in Django Admin

Open admin.py:

from django.contrib import admin
from .models import Classroom, Student, Course, UserProfile

admin.site.register(Classroom)
admin.site.register(Student)
admin.site.register(Course)
admin.site.register(UserProfile)

Start your development server:

python manage.py runserver

Open /admin/ in your browser and log in with a superuser account.

You can now manage classroom, student, course, and profile records from the admin panel.

6. Display Related Data in a Template

Suppose your view needs to display a classroom and its students.

views.py

from django.shortcuts import render, get_object_or_404
from .models import Classroom


def classroom_detail(request, classroom_id):
    classroom = get_object_or_404(
        Classroom.objects.prefetch_related("students"),
        id=classroom_id
    )

    return render(
        request,
        "classroom_detail.html",
        {"classroom": classroom}
    )

urls.py

from django.urls import path
from . import views

urlpatterns = [
    path(
        "classrooms/<int:classroom_id>/",
        views.classroom_detail,
        name="classroom_detail"
    ),
]

Include this app's URL configuration in your project's main urls.py if you have not already done so.

classroom_detail.html

<h1>{{ classroom.name }}</h1>

<p>Room Number: {{ classroom.room_number }}</p>

<h2>Students</h2>

<ul>
    {% for student in classroom.students.all %}
        <li>{{ student.name }} — {{ student.email }}</li>
    {% empty %}
        <li>No students have been added to this classroom.</li>
    {% endfor %}
</ul>

This template uses the related_name="students" setting to retrieve students connected to the classroom.

7. Choosing the Correct Relationship

Relationship Meaning Example
ForeignKey Many-to-one Many students belong to one classroom
OneToOneField One-to-one One user has one profile
ManyToManyField Many-to-many Students enroll in multiple courses

Choose the relationship based on the real business rule, not just the number of tables.

8. Common Errors and Solutions

Error 1: Cannot resolve keyword

Cause: The query uses an incorrect field name or related name.

Solution: Check your model fields and the related_name argument.

Error 2: No such table

Cause: The database does not have the table created for the model.

Solution:

python manage.py makemigrations
python manage.py migrate

Error 3: IntegrityError when creating a Student

Cause: A required ForeignKey value is missing, or a unique field such as email already exists.

Solution: Supply a valid classroom and a unique email address when creating a student.

Error 4: Cannot assign an integer to a ForeignKey field

Cause: Code assigns a raw integer to the relationship field instead of a model instance.

Incorrect:

student.classroom = 1

Correct:

student.classroom = Classroom.objects.get(id=1)

Alternatively, you can assign the related object's ID directly:

student.classroom_id = 1

Ensure that the referenced classroom exists.

Error 5: RelatedObjectDoesNotExist

Cause: Code attempts to access a reverse one-to-one object that has not been created.

Solution: Create the related profile when needed and handle cases where it does not exist.

9. Frequently Asked Questions

Q1. What is a ForeignKey in Django?

A ForeignKey represents a many-to-one relationship between database records.

Q2. What is the purpose of OneToOneField?

It establishes a one-to-one relationship, such as a user and a profile.

Q3. What is ManyToManyField used for?

It represents a relationship where multiple records on both sides can be connected, such as students and courses.

Q4. What does on_delete=models.CASCADE do?

It deletes the dependent records when the referenced object is deleted. Use it only when this behavior matches your application's requirements.

Q5. What is related_name?

It defines the name used to access related objects from the other side of a relationship.

Q6. What is the difference between select_related() and prefetch_related()?

select_related() uses SQL joins for single-valued relationships such as foreign keys and one-to-one fields. prefetch_related() performs additional queries and joins the results in Python, making it useful for many-to-many and reverse relationships.

Q7. Do model relationship changes require migrations?

Yes. Changes to model fields and relationship definitions generally require migrations.

10. Django Interview Questions and Answers

Q1. Explain the three major relationship fields in Django.

ForeignKey represents many-to-one, OneToOneField represents one-to-one, and ManyToManyField represents many-to-many relationships.

Q2. Why is on_delete required for ForeignKey?

It defines what happens to dependent records when the referenced object is deleted.

Q3. What is a reverse relationship?

It allows you to access related records from the referenced object's side. For example, classroom.students.all() retrieves students through the configured related_name.

Q4. What is the difference between related_name and related_query_name?

related_name controls the reverse attribute used to access related objects. related_query_name controls the name used for reverse relationship lookups in ORM queries.

Q5. Why use prefetch_related()?

It can reduce repeated database queries when retrieving collections of related objects.

Q6. How does Django implement ManyToManyField?

Django normally creates an intermediate join table that stores the relationships between the two models.

Q7. When would you use a custom intermediate model?

Use a custom through model when the relationship itself needs extra data, such as enrollment date, grade, role, or status.

11. Practical Assignment

Build a small Student Course Management System with these requirements:

  1. Create a Classroom model with name and room number.

  2. Create a Student model with name, email, and a classroom foreign key.

  3. Create a Course model with a many-to-many relationship to students.

  4. Create a UserProfile model connected to the configured user model using OneToOneField.

  5. Register all models in Django Admin.

  6. Add at least two classrooms, three students, and two courses.

  7. Create a classroom detail page that displays the students assigned to that classroom.

  8. Display the courses associated with each student.

  9. Use select_related() and prefetch_related() appropriately.

  10. Test what happens when related records are missing or deleted.

Conclusion

Django model relationships let you represent real-world connections between data. Use ForeignKey for many-to-one relationships, OneToOneField for one-to-one relationships, and ManyToManyField for many-to-many relationships.

Understanding these relationships is essential for building scalable applications, including school management systems, hospital management systems, e-commerce websites, and learning management platforms.

Next Module: Django QuerySets and ORM — Filtering, Searching, Ordering, Aggregation, and Advanced Queries.