Relationships Basics

Link models with ForeignKey, OneToOneField, and ManyToManyField. Understand on_delete, related_name, related_query_name, and reverse accessors.

1. Introduction

Most Django projects have models that are connected to each other. A blog post belongs to a category. A user has one profile. A product can have many tags. Django provides three relationship fields to handle these connections: ForeignKey, OneToOneField, and ManyToManyField.

This guide covers how each relationship works, when to use each one, and the key options like on_delete, related_name, and reverse accessors.

  • You should already be comfortable defining basic models and fields.
  • Your .venv must be active and Django 5.2 installed.

2. ForeignKey — many to one

Use ForeignKey when many records in one model belong to one record in another. For example, many articles belong to one category.

# pages/models.py

from django.db import models


class Category(models.Model):
    title = models.CharField(max_length=100)

    def __str__(self):
        return self.title


class Article(models.Model):
    category = models.ForeignKey(
        Category,
        on_delete=models.CASCADE,
        related_name='articles',
    )
    title = models.CharField(max_length=200)
    content = models.TextField()

    def __str__(self):
        return self.title

In the database, Django adds a category_id column to the Article table that stores the primary key of the related Category row.

Accessing the relationship

# Forward — get the category of an article
article = Article.objects.get(id=1)
article.category        # Category object
article.category.title  # 'Technology'

# Reverse — get all articles in a category
category = Category.objects.get(id=1)
category.articles.all()  # QuerySet of Article objects

3. on_delete — what happens when the parent is deleted

on_delete is required on every ForeignKey. It tells Django what to do with the related records when the parent record is deleted.

  • CASCADE — delete all related records too. Use this when the child cannot exist without the parent. For example, deleting a category deletes all its articles.
  • SET_NULL — set the foreign key to NULL. Requires null=True on the field. Use when the child can exist without a parent.
  • SET_DEFAULT — set the foreign key to the field's default value. Requires a default to be set.
  • PROTECT — prevent deletion of the parent if any related records exist. Django raises a ProtectedError.
  • DO_NOTHING — do nothing in Django. The database constraint handles it. Use with caution — can leave orphaned records.
# Delete articles when category is deleted
category = models.ForeignKey(Category, on_delete=models.CASCADE)

# Set to NULL when author is deleted (author is optional)
author = models.ForeignKey(
    'auth.User',
    on_delete=models.SET_NULL,
    null=True,
    blank=True,
)

# Prevent deletion if articles exist
category = models.ForeignKey(Category, on_delete=models.PROTECT)

5. OneToOneField — one to one

Use OneToOneField when one record in a model is linked to exactly one record in another model — and vice versa. The most common use case is extending the built-in User model with a Profile.

class Profile(models.Model):
    user = models.OneToOneField(
        'auth.User',
        on_delete=models.CASCADE,
        related_name='profile',
    )
    bio = models.TextField(blank=True)
    photo = models.ImageField(upload_to='profiles/', blank=True)

    def __str__(self):
        return f'Profile of {self.user.username}'

Accessing the relationship

# Forward
profile = Profile.objects.get(user=request.user)

# Reverse — access profile directly from the user object
request.user.profile
request.user.profile.bio

6. ManyToManyField — many to many

Use ManyToManyField when records on both sides can be related to many records on the other side. For example, an article can have many tags and a tag can belong to many articles.

class Tag(models.Model):
    name = models.CharField(max_length=50, unique=True)

    def __str__(self):
        return self.name


class Article(models.Model):
    title = models.CharField(max_length=200)
    tags = models.ManyToManyField(Tag, related_name='articles', blank=True)

    def __str__(self):
        return self.title

Django creates a separate junction table in the database to store the relationship — you do not need to create it yourself. The table is named appname_modelname_fieldname, for example pages_article_tags.

Managing the relationship

article = Article.objects.get(id=1)
tag = Tag.objects.get(name='django')

# Add a tag
article.tags.add(tag)

# Remove a tag
article.tags.remove(tag)

# Get all tags for an article
article.tags.all()

# Get all articles with a specific tag (reverse)
tag.articles.all()

7. Referencing models as strings

If a model references another model defined later in the same file, or in a different app, you can use a string reference instead of the class directly:

# Using the class directly (model must be defined above)
category = models.ForeignKey(Category, on_delete=models.CASCADE)

# Using a string (order does not matter, works across apps)
category = models.ForeignKey('pages.Category', on_delete=models.CASCADE)

# Referencing Django's built-in User model
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)

The string format is 'appname.ModelName'. For models in the same app you can omit the app name and just use 'ModelName'.

8. Next steps

You now understand the three relationship types and how to use them. The next step covers many-to-many with through models — for when you need to store extra data on the relationship itself.


Never miss a story on Django.wiki

Subscribe for fresh tutorials, snippets, and updates.

By subscribing you agree to our Privacy Policy.