I am trying to optimize database queries for a Django application. Here's a simplified example:
class Label(models.Model): name = models.CharField(max_length=200)
I have a function that retrieves all Label and Thing and puts them in a JSON data structure in which Thing refers to Label using their id (primary keys). Something like that:
{ 'labels': [ { 'id': 123, 'name': 'label foo' }, ... ], 'things': [ { 'id': 45, 'name': 'thing bar', 'labels': [ 123, ... ] }, ... ] }
What is the most efficient way to get such a data structure using Django? Suppose I have L Label and T Thing s, and the middle Thing has x Label s.
Method 1:
data = {} data['labels'] = [model_to_dict(label) for label in Label.objects.all()] data['things'] = [model_to_dict(thing) for thing in Thing.objects.all()]
This makes database queries (1 + 1 + T ), since model_to_dict(thing) needs to select a Label for each Thing individually.
Method 2:
data = {} data['labels'] = [model_to_dict(label) for label in Label.objects.all()] data['things'] = [model_to_dict(thing) for thing in Thing.objects.prefetch_related('labels').all()]
This only queries the database (1 + 1 + 1), since the Thing selection now has its own Label , preselected in one additional query.
This is still not satisfactory. prefetch_related('labels') will retrieve many copies of the same Label , whereas I only need their id s. Is there a way to prefetch id only Label ? I tried prefetch_related('labels__id') , but that didn't work. I'm also worried that since T is large (hundreds), prefetch_related('labels') leads to an SQL query with a big IN clause. L is much smaller (<10), so I could do this instead:
Method 3:
data = {} data['labels'] = [model_to_dict(label) for label in Label.objects.prefetch_related('thing_set').all()] things = list(Thing.objects.all())
This leads to a smaller IN clause, but still unsatisfactory because prefetch_related('thing_set') selects a duplicate of Thing s if a Thing has multiple Label s.
Summary:
Label and Thing are connected by a ManyToManyField . I still get all Label and Thing . So, how can I effectively use their many-to-many relationships?