Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding ManyToMany fields in Django with a through model

I'm having trouble understanding the use of ManyToMany models fields with a through model. I can easily achieve the same without the ManyToMany field. Considering the following from Django's docs:

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


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


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)

What I don't understand, is how is using the ManyToMany field better than simply dropping it and using the related manager. For instance, the two models will change to the following:

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


class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE, related_name='members')
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

What am I missing here?

like image 361
Kritz Avatar asked Jun 06 '16 05:06

Kritz


People also ask

What is through in ManyToMany Django?

Django through option is used when you manually want to specify the intermediary table to manage many-to-many relationships.

What is a through model Django?

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.

What's an example of a many-to-many relationship that would work well with Django's ManyToManyField?

When you design a database for a large product, it is inevitable to arrive at a point where you have two models that are related to each other in a way that does not get solved using a ForeignKey alone. A good example of a many-to-many relationship is the relationship between a sandwich and a sauce.


2 Answers

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.

like image 100
Kevin Christopher Henry Avatar answered Oct 10 '22 13:10

Kevin Christopher Henry


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.

like image 12
Alexandre Pouzikov Avatar answered Oct 10 '22 14:10

Alexandre Pouzikov