I have 2 models in my django code:
class ModelA(models.Model):
name = models.CharField(max_length=255)
description = models.CharField(max_length=255)
created_by = models.ForeignKey(User)
class ModelB(models.Model):
category = models.CharField(max_length=255)
modela_link = models.ForeignKey(ModelA, 'modelb_link')
functions = models.CharField(max_length=255)
created_by = models.ForeignKey(User)
Say ModelA has 100 records, all of which may or may not have links to ModelB
Now say I want to get a list of every ModelA record along with the data from ModelB
I would do:
list_a = ModelA.objects.all()
Then to get the data for ModelB I would have to do
for i in list_a:
i.additional_data = i.modelb_link.all()
However this runs a query on every instance of i. Thus making 101 queries to run.
Is there any way of running this all in just 1 query. Or at least less than the 101 queries.
I've tried putting in ModelA.objects.select_related().all()
but this didn't seem to have any effect.
Thanks
As Ofri says, select_related
only works on forwards relations, not reverse ones.
There's no built-in way to automatically follow reverse relations in Django, but see my blog post for a technique to do it reasonably efficiently. The basic idea is to get all the related objects for every item at once, then associate them manually with their related item - so you can do it in 2 queries rather than n+1.
Django ORM is a good thing but some some things is better to do manually.
You may import connection cursor and execute raw sql in single query.
from django.db import connection
cur=connection.cursor()
cur.execute(query)
rows = cur.fetchall()
your query should look like (for MySQL)
SELECT * FROM appname_modela INNER JOIN appname_modelb ON appname_modela.id=appname_modelb.modela_link_id
The reason .select_related() didn't work, is that .select_related() is used to follow foreign keys. Your ModelA doesn't have a foreign key to ModelB. Its ModelB that has a foreign key to ModelA. (so a ModelA instance can have multiple ModelB instances related to it).
You could use this to do it in 2 queries, and a bit of python code:
list_b = ModelB.objects.all()
list_a = ModelA.objects.all()
for a in list_a:
a.additional_data = [b for b in list_b if b.modela_link_id==a.id]