Sometimes you want to connect two things with extra details about their connection. A through model lets you add those extra details between two related items.
Through model for extra fields on M2M in Django
class ModelA(models.Model): name = models.CharField(max_length=100) class ModelB(models.Model): name = models.CharField(max_length=100) related_as = models.ManyToManyField(ModelA, through='ThroughModel') class ThroughModel(models.Model): modela = models.ForeignKey(ModelA, on_delete=models.CASCADE) modelb = models.ForeignKey(ModelB, on_delete=models.CASCADE) extra_field = models.CharField(max_length=100)
The through attribute tells Django to use your custom model for the relationship.
In the through model, you must have foreign keys to both related models.
role describing their job.class Book(models.Model): title = models.CharField(max_length=100) class Author(models.Model): name = models.CharField(max_length=100) books = models.ManyToManyField(Book, through='Authorship') class Authorship(models.Model): author = models.ForeignKey(Author, on_delete=models.CASCADE) book = models.ForeignKey(Book, on_delete=models.CASCADE) role = models.CharField(max_length=50) # e.g., 'Writer', 'Editor'
date_joined field.class Student(models.Model): name = models.CharField(max_length=100) class Course(models.Model): title = models.CharField(max_length=100) students = models.ManyToManyField(Student, through='Enrollment') class Enrollment(models.Model): student = models.ForeignKey(Student, on_delete=models.CASCADE) course = models.ForeignKey(Course, on_delete=models.CASCADE) date_joined = models.DateField()
This example shows a sports team and players linked with extra info like when a player joined and their position.
from django.db import models class Player(models.Model): name = models.CharField(max_length=100) class Team(models.Model): name = models.CharField(max_length=100) players = models.ManyToManyField(Player, through='Membership') class Membership(models.Model): player = models.ForeignKey(Player, on_delete=models.CASCADE) team = models.ForeignKey(Team, on_delete=models.CASCADE) date_joined = models.DateField() position = models.CharField(max_length=50) # Usage example (not part of models): # Create objects and link with extra info # p = Player.objects.create(name='Alice') # t = Team.objects.create(name='Red Dragons') # Membership.objects.create(player=p, team=t, date_joined='2024-01-15', position='Captain')
You cannot add extra fields directly on a normal ManyToManyField without a through model.
When using a through model, you manage the relationship by creating and updating the through model instances.
Remember to use on_delete=models.CASCADE to keep data consistent when deleting linked objects.
A through model lets you add extra details to a many-to-many relationship.
You create a separate model with foreign keys to both related models and extra fields.
Use the through parameter on ManyToManyField to connect your custom model.