https://docs.djangoproject.com/en/2.0/topics/db/models/#extra-fields-on-many-to-many-relationships
Extra fields on many-to-many relationships¶
When you’re only dealing with simple many-to-many relationships such asmixing and matching pizzas and toppings, a standardManyToManyField
is all you need. However, sometimesyou may need to associate data with the relationship between two models.
For example, consider the case of an application tracking the musical groupswhich musicians belong to. There is a many-to-many relationship between a personand the groups of which they are a member, so you could use aManyToManyField
to represent this relationship.However, there is a lot of detail about the membership that you might want tocollect, such as the date at which the person joined the group.
For these situations, Django allows you to specify the model that will be usedto govern the many-to-many relationship. You can then put extra fields on theintermediate model. The intermediate model is associated with theManyToManyField
using thethrough
argument to point to the modelthat will act as an intermediary. For our musician example, the code would looksomething like this:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
When you set up the intermediary model, you explicitly specify foreignkeys to the models that are involved in the many-to-many relationship. Thisexplicit declaration defines how the two models are related.
There are a few restrictions on the intermediate model:
- Your intermediate model must contain one - and only one - foreign keyto the source model (this would be
Group
in our example), or you mustexplicitly specify the foreign keys Django should use for the relationshipusingManyToManyField.through_fields
.If you have more than one foreign key andthrough_fields
is notspecified, a validation error will be raised. A similar restriction appliesto the foreign key to the target model (this would bePerson
in ourexample). - For a model which has a many-to-many relationship to itself through anintermediary model, two foreign keys to the same model are permitted, butthey will be treated as the two (different) sides of the many-to-manyrelationship. If there are more than two foreign keys though, youmust also specify
through_fields
as above, or a validation errorwill be raised. - When defining a many-to-many relationship from a model toitself, using an intermediary model, you must use
symmetrical=False
(seethe model field reference).
Now that you have set up your ManyToManyField
to useyour intermediary model (Membership
, in this case), you’re ready to startcreating some many-to-many relationships. You do this by creating instances ofthe intermediate model:
>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(person=ringo, group=beatles,
... date_joined=date(1962, 8, 16),
... invite_reason="Needed a new drummer.")
>>> m1.save()
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>]>
>>> ringo.group_set.all()
<QuerySet [<Group: The Beatles>]>
>>> m2 = Membership.objects.create(person=paul, group=beatles,
... date_joined=date(1960, 8, 1),
... invite_reason="Wanted to form a band.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>]>