Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid n+1 select in django?

Tags:

join

orm

django

I have a very simple datamodel with a one to many relationship between video and comments

class Video(models.Model):
    url = models.URLField(unique=True)
    .....

class Comment(models.Model):
    title = models.CharField(max_length=128)
    video = models.ForeignKey('Video')
        .....

I want to query for videos and grab the whole object graph (videos with all the comments). Looking at the sql, I see it does two selects, one for the Videos and one for the Comments. How do I avoid that? I want to do a join and grab everything at once.

Is it possible to do this with django?

like image 739
bvk Avatar asked Jun 17 '11 16:06

bvk


People also ask

What is N 1 query in Django?

N+1 is a very common data-access pattern problem where a query is executed for every result of a previous query. If the definition looks cryptic, let's understand this problem with an example.

What is the N 1 selects problem in ORM?

The N+1 query problem happens when the data access framework executed N additional SQL statements to fetch the same data that could have been retrieved when executing the primary SQL query. The larger the value of N, the more queries will be executed, the larger the performance impact.

How does exclude work in Django?

The exclude() method from the QuerySet class returns all objects that do not match the given keyword arguments. So whatever data matches the parameter that is passed into the exclude() method will be excluded from the returned QuerySet.

What does .values do in Django?

The values_list() method allows you to return only the columns that you specify.


1 Answers

For ForeignKey, you can use selected_related():

Comment.objects.select_related('video').all()

It will generate one query only, gathering coments for you as well as videos.

For something more complex (such as M2M), you need an external app such as unjoinify to make optimizations but it uses SQL queries to then put them back in objects.

If you are unconfortable with this (I am), you have some alternatives:

  • django-queryset-transform: not a full solution, but helps
  • django-batch-select: roughtly a select_related that works with M2M and reverse relations.
like image 161
e-satis Avatar answered Sep 18 '22 06:09

e-satis