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?
Django through option is used when you manually want to specify the intermediary table to manage many-to-many relationships.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With