Storing Extra Data in Django Join Tables
I’ve had some people ask me about the
through argument supported by Django’s
ManyToManyField class. This option supports a very simple use case: when you want to store additional data in a join table.
Imagine, for instance, that we’re building a simple course registration tool. Let’s call the Django app
courses. We’ll define the following models:
from django.db import models class Course(models.Model): """A course offered at our school.""" name = models.CharField(max_length=100) code = models.CharField(max_length=5, unique=True) description = models.TextField() def __unicode__(self): return u'%s: %s' % (self.code, self.name) class Student(models.Model): """A student registered with our school.""" first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) number = models.CharField(max_length=10, unique=True) courses = models.ManyToManyField(Course, null=True) registered_on = models.DateTimeField(auto_now_add=True) def __unicode__(self): return u'%s -> %s, %s' % ( self.number, self.last_name, self.first_name)
When we run
syncdb or generate a migration for these
models, Django will automatically create the following tables:
courses_course, courses_student, and courses_student_courses. The first two should be
self-explanatory. The third table will store information about our
many-to-many relationship. Each row will associate a student id with a
Now let’s say we want to store the date and time whenever a student registers for a course. We’d want something like the following model:
class Registration(models.Model): """Student registration for a course.""" student = models.ForeignKey(Student) course = models.ForeignKey(Course) registered_on = models.DateTimeField(auto_now_add=True)
In order to get Django to use our model as a join table, we can use
through by changing a single line in our student model:
class Student(models.Model): ... courses = models.ManyToManyField(Course, null=True, through='Registration') ...
And that’s it. Now, Django will automatically generate a
table and use it instead of
courses_student_courses, allowing us to store
extra data about the registration.