Django by Example: Many To Many Mapping
Django 5.0+
Many-to-Many relationships occur when multiple records in one table are associated with multiple records in another. This sample code shows how Django handles the intermediate "join table" automatically, or allows you to define a custom one.
Code
from django.db import models
class Tag(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
# Simple ManyToMany
tags = models.ManyToManyField(Tag, related_name='articles')
# Custom Intermediate Model (Through Model)
class Student(models.Model):
name = models.CharField(max_length=100)
class Course(models.Model):
name = models.CharField(max_length=100)
# Define the relationship through a custom model
students = models.ManyToManyField(
Student,
through='Enrollment',
related_name='courses'
)
class Enrollment(models.Model):
student = models.ForeignKey(Student, on_delete=models.CASCADE)
course = models.ForeignKey(Course, on_delete=models.CASCADE)
date_enrolled = models.DateField()
grade = models.CharField(max_length=2, blank=True)
class Meta:
unique_together = [['student', 'course']]
# Usage:
# article = Article.objects.create(title="Django Tips")
# tag1 = Tag.objects.create(name="Python")
# article.tags.add(tag1) # Add relationship
#
# Custom Through:
# s1 = Student.objects.create(name="Bob")Explanation
Many-to-Many relationships are used when an item in one table can be related to multiple items in another, and vice-versa. Django handles this by creating an intermediate "join table" automatically. However, if you need to store extra data about the relationship itself (e.g., date_enrolled or grade), you must define a custom intermediate model using the through argument.
Key considerations:
- Symmetrical: By default, ManyToManyFields on the same model are symmetrical (if I am your friend, you are mine). You can disable this with
symmetrical=False. - Prefetching: Because these relationships span multiple tables, they are prone to performance issues. Always use
prefetch_related()(notselect_related) to optimize queries. - Through Models: When using a custom through model, you cannot use the simple
add(),create(), orremove()methods. You must create instances of the intermediate model directly.
Code Breakdown
27-32
constraints allows defining database-level constraints like CHECK constraints. This ensures data integrity at the database level, regardless of the application logic.
