Should I use a ‘through’ table for my Model to Model relation? It was never a question for me two years ago.
Why would you consider a ‘through’ table?
What if I need to add characteristics to the Model-To-Model relation??
It sounds all so magical – you have this tiny itty-bitty table with an additional property, say, to make the front-end design look good.
Why not make a ‘through’ table?
In reality, making redundant tables can work, but to an extent – your server’s RAM won’t last forever.
Once your client returns to this feature later, they will exclaim that you did something wrong, but in reality you didn’t change a bit. Now comes a process of simplification, that most likely means reducing this feature altogether and rewriting HTML on your own to fit your RAM limits.
What should I pick?
It there are quite a few design features that require me to use it, then I should use a through table.
If you see that the feature(s) are redundant – try to change your client’s mind. If you can’t – just do it and make it work fast.
What is the difference?
This list is being updated upon new discoveries.
1. Accesing data
For simple ManyToManyField
you can use related_name='some_related_name'
and access this Queryset
as myinstance.some_related_name.all()
.
But if you have a through='some_table'
, you will have to add related_name to the original’s field in the through table and access these relations from that point. Your access will actually work backwards.
For families of models it is easier to use related_name='%(app_label)s_%(class)s'
2. Making changes to the ManyToMany field while changing it.
Say, you have a M2M field and a property One. Based on the value of the property One, you want to make changes to the M2M field. If you are changing M2M as well, say, if you are creating an instance and both values are having something in them. As far as I know, you can’t really do that.
3. Saving overall
Saving overall is much easier, as every relations is its own instance. But you have to curate it. You can’t forget to create one or update – make a QuerySet
/Manager
or do it manually. For example, a QuerySet override:
class CarQuerySet(models.QuerySet):
def create(self, **kwargs):
with transaction.atomic():
car = self.model(**kwargs)
car.save(force_insert=True)
CarProfile.objects.create(car=car)
return car
4. Ordering (upd. 03.07)
Say, you have some paired data like names and telephone numbers. You want to store them and use later. You may think, hey, Django says that ManyToManyField
has a tuple, so I will just use two M2M
fields for names and telephon numbers and get them later in the same order I added them in. WRONG! Think again.
I just encountered this issue and were pretty devastated about it. So, it is 100% correct that I am adding names and numbers in the right order.
names = ['Kate', 'Maria', 'Petra']
tels = ['Kate\'s number', 'Maria\'s number', 'Petra\'s number']
# my_obj has names and telephone_numbers M2M fields
[my_obj.names.add(x) for x in names]
[my_obj.telephone_numbers.add(x) for x in tels]
It may work on modest amounts of data. But at some point, say, even 10 units, you may discover that M2M objects don’t keep the order that you add them in.
Conclusion
Do not bend under unrealistic expectations. It is being said that your business logic must be neat and smart, but achieving that is highly unreasonable when the original design is excessive.