Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

prefetch_related for multiple Levels

If my Models look like:

class Publisher(models.Model):     pass  class Book(models.Model):     publisher = models.ForeignKey(Publisher)  class Page(models.Model):     book = models.ForeignKey(Book) 

and I would like to get the queryset for Publisher I do Publisher.object.all(). If then want to make sure to prefetch I can do:

Publisher.objects.all().prefetch_related('book_set')` 

My questions are:

  1. Is there a way to do this prefetching using select_related or must I use prefetch_related?
  2. Is there a way to prefetch the page_set? This does not work:

Publisher.objects.all().prefetch_related('book_set', 'book_set_page_set')

like image 311
Alex Rothberg Avatar asked Nov 25 '14 00:11

Alex Rothberg


People also ask

What is difference between select related and Prefetch_related?

select_related() "follows" foreign-key relationships, selecting additional related-object data when it executes its query. prefetch_related() does a separate lookup for each relationship, and does the "joining" in Python.

What is Prefetch_related?

In Django, select_related and prefetch_related are designed to stop the deluge of database queries that are caused by accessing related objects. In this article, we will see how it reduces the number of queries and make the program much faster.


2 Answers

Since Django 1.7, instances of django.db.models.Prefetch class can be used as an argument of .prefetch_related. Prefetch object constructor has a queryset argument that allows to specify nested multiple levels prefetches like that:

Project.objects.filter(         is_main_section=True     ).select_related(         'project_group'     ).prefetch_related(         Prefetch(             'project_group__project_set',             queryset=Project.objects.prefetch_related(                 Prefetch(                     'projectmember_set',                     to_attr='projectmember_list'                 )             ),             to_attr='project_list'         )     ) 

It is stored into attributes with _list suffix because I use ListQuerySet to process prefetch results (filter / order).

like image 179
Dmitriy Sintsov Avatar answered Sep 18 '22 12:09

Dmitriy Sintsov


  1. No, you cannot use select_related for a reverse relation. select_related does a SQL join, so a single record in the main queryset needs to reference exactly one in the related table (ForeignKey or OneToOne fields). prefetch_related actually does a totally separate second query, caches the results, then "joins" it into the queryset in python. So it is needed for ManyToMany or reverse ForeignKey fields.

  2. Have you tried two underscores to do the multi level prefetches? Like this: Publisher.objects.all().prefetch_related('book_set', 'book_set__page_set')

like image 37
jproffitt Avatar answered Sep 16 '22 12:09

jproffitt