[Django]-Understanding ManyToMany fields in Django with a through model

68👍

You’re right, if you define the membership table explicitly then you don’t need to use a ManyToManyField.

The only real advantage to having it is if you’d find the related manager convenient. That is, this:

group.members.all()  # Persons in the group

looks nicer than this:

Person.objects.filter(membership_set__group=group)  # Persons in the group

In practice, I think the main reason for having both is that often people start with a plain ManyToManyField; realize they need some additional data and add the table explicitly; and then continue to use the existing manager because it’s convenient.

24👍

So I just wanted to add to anyone who is looking at this and may want another example to save them research. For one, I think it’s important to note that in OP’s questions, he should of removed the Group model not the People model and removed the matching field from the Membership model. That way, the model goes back to it’s original meaning.

When looking at a many-to-many relationship, the through field can almost be contrived as the "why" to the many-to-many relationship. If we give the nomenclature a different name, it might change what the reader sees:

class Person(models.Model):
    name = models.CharField(max_length=128)


class Club(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='RegistrationReceipt')


class RegistrationReceipt(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    club = models.ForeignKey(Club, on_delete=models.CASCADE)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)
    paid_dues = models.BooleanField(default = True)
    fee_payment_date = models.DateTimeField() 

Now, you can imagine yourself adding all sorts of logic whenever a member joins this club. When they joined? Why did they join? Did they pay? When is their payment date? etc. You can obviously tackle this relationship in different ways, but you can see more clearly the use of "through" in a Many-to-Many relationship.

Also, for those that know SQL. The through attribute/field is the way you customize the intermediary table, the one that Django creates itself, that one is what the through field is changing.

-3👍

I have some problem with the answer from Kevin Christopher Henry.

I don’t think that the equivalent of the group.members.all() without a through="members" is Person.objects.....

Instead I think it is group.person_set.all() if the M2M field is on Person side. Or group.persons.all() if the M2M field is inside Group.

But I think without through=.. you have no control over the created table. It contains and will contain just 2 fields: both ID’s of the related rows.

But with through=.. you can create the model yourself and add (now or later) the additional fields, which often can have a good reason. Example of such field: valid_from = DateField(), or so.

👤mirek

Leave a comment