How do Django model relationships work (ForeignKey, ManyToMany, OneToOne)?
Learn how Django ForeignKey, ManyToManyField, and OneToOneField map database relationships, on_delete rules, reverse accessors, and efficient queries.
Expected Interview Answer
Django models express relational database relationships through three field types: ForeignKey for many-to-one, ManyToManyField for many-to-many, and OneToOneField for one-to-one, and the ORM turns these into the correct foreign keys and join tables automatically.
A ForeignKey stores a single link on the 'many' side and requires an on_delete rule such as CASCADE or PROTECT. A ManyToManyField creates a hidden junction table so many rows on each side can relate freely, and you can supply a custom through model to add extra columns. A OneToOneField is a ForeignKey with a unique constraint, typically used to extend or split a model. Each relationship also exposes a reverse accessor (via related_name) so you can traverse it from the other side.
- Maps database keys and join tables without writing SQL
- Enforces referential integrity through on_delete rules
- Enables reverse lookups with related_name
- Supports extra data on relationships via through models
- Keeps queries expressive with select_related and prefetch_related
AI Mentor Explanation
A ForeignKey is like each player belonging to exactly one team while a team fields many players, so the link lives on the player. A ManyToManyField is like players and the tournaments they have appeared in, tracked in a separate registry sheet. A OneToOneField is like a captain: exactly one per team and one team per captain, a unique pairing you can look up from either side.
Step-by-Step Explanation
Step 1
Pick the relationship type
Choose ForeignKey for many-to-one, ManyToManyField for many-to-many, and OneToOneField for a unique one-to-one link.
Step 2
Declare the field with a target
Point the field at the related model, using a string name for models defined later or in another app.
Step 3
Set on_delete for keyed fields
ForeignKey and OneToOneField require on_delete, such as CASCADE, PROTECT, or SET_NULL, to define delete behaviour.
Step 4
Add related_name for reverse access
Name the reverse accessor so traversing from the other side is readable and avoids clashes.
Step 5
Migrate and query efficiently
Run makemigrations and migrate, then use select_related for FK/O2O and prefetch_related for M2M to avoid N+1 queries.
What Interviewer Expects
- Correct mapping of each field type to its database structure
- Knowing on_delete is mandatory on ForeignKey and OneToOneField
- Understanding the hidden junction table behind ManyToManyField
- Awareness of reverse accessors and related_name
- Choosing select_related vs prefetch_related to avoid N+1 queries
Common Mistakes
- Forgetting to specify on_delete on a ForeignKey
- Confusing ManyToManyField with two ForeignKeys
- Believing a ManyToManyField needs a manual join table when Django creates one
- Ignoring related_name and getting reverse accessor clashes
- Using ForeignKey where OneToOneField's uniqueness is required
Best Answer (HR Friendly)
“Django lets you describe how records connect to each other using three link types: one for many-to-one links, one for many-to-many links, and one for strict one-to-one links. Django then builds the right database structure automatically, so developers work with clean Python objects instead of raw SQL.”
Code Example
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
class Profile(models.Model):
# OneToOne: one profile per author
author = models.OneToOneField(Author, on_delete=models.CASCADE, related_name='profile')
bio = models.TextField()
class Book(models.Model):
# ForeignKey: many books, one author
author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')
title = models.CharField(max_length=200)
# ManyToMany: a book has many tags, a tag many books
tags = models.ManyToManyField('Tag', related_name='books')
class Tag(models.Model):
name = models.CharField(max_length=50)
# Traversing relationships
author = Author.objects.select_related('profile').get(pk=1)
author.books.all() # reverse ForeignKey
author.profile.bio # reverse OneToOne
book = Book.objects.prefetch_related('tags').first()Follow-up Questions
- What does the on_delete argument control and what options exist?
- How do you add extra fields to a ManyToMany relationship?
- What is the difference between select_related and prefetch_related?
- When would you use a OneToOneField instead of a ForeignKey?
- How does related_name change reverse queries?
MCQ Practice
1. Which field type requires an on_delete argument?
Both ForeignKey and OneToOneField create a database foreign key, so Django requires on_delete to define what happens when the referenced row is deleted.
2. What does Django create behind a ManyToManyField?
A ManyToManyField is backed by a separate junction table holding pairs of keys, unless you supply a custom through model.
3. To add columns to a many-to-many link you should use?
Passing through=YourModel lets the junction table carry extra fields like a date joined or a role.
Flash Cards
What database structure does a ForeignKey create? — A single foreign-key column on the many side pointing at the target's primary key.
What is a OneToOneField really? — A ForeignKey with a unique constraint, giving a strict one-to-one relationship.
How do you add extra data to a ManyToMany link? — Define a custom through model and pass it via the through argument.
What does related_name do? — It names the reverse accessor used to query the relationship from the other model.