2014-11-25 3 views
12

Если мои модели выглядят следующим образом:prefetch_related для нескольких уровней

class Publisher(models.Model): 
    pass 

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

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

, и я хотел бы получить QuerySet для Publisher я делаю Publisher.object.all(). Если же хотите, чтобы убедиться в упреждением я могу сделать:

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

Мои вопросы:

  1. Есть ли способ сделать это с помощью предзапросы select_related или я должен использовать prefetch_related?
  2. Есть ли способ предварительной выборки page_set? Это не работает:

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

ответ

19
  1. Нет, вы не можете использовать select_related для обратной связи. select_related выполняет SQL-соединение, поэтому одна запись в основном запросе требует ссылки только на одну из связанной таблицы (ForeignKey или OneToOne полей). prefetch_related фактически выполняет полностью отдельный второй запрос, кэширует результаты, затем «присоединяет» его к запросу в python. Так что это необходимо для ManyToMany или наоборот ForeignKey полей.

  2. Вы пробовали два символа подчеркивания, чтобы сделать предварительные выборки на нескольких уровнях? Как это: Publisher.objects.all().prefetch_related('book_set', 'book_set__page_set')

+3

2. Двойное подчеркивание работает. –

+0

1. Если '' 'имеет поле' OneToOne' для 'TextContent', было бы:' ... prefetch_related ('book_set__page_set__text_contents') 'или' ... select_related ('book_set__page_set__text_contents') ' –

+0

Я считаю, что это быть второй версией. – jproffitt

4

Так как Django 1.7, экземпляры класса django.db.models.Prefetch могут быть использованы в качестве аргумента .prefetch_related. Prefetch конструктор объекта имеет queryset аргумент, позволяющий указать несколько вложенных уровней предварительную выборку так:

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' 
     ) 
    ) 

Он хранится в атрибутах с _list суффикса, потому что я использую ListQuerySet для обработки результатов предварительной выборки (фильтр/заказ).

Смежные вопросы