prefetch_related用于多个级别

时间:2014-11-25 00:30:52

标签: django django-queryset django-orm

如果我的模特看起来像:

class Publisher(models.Model):
    pass

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

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

我想获得PublisherPublisher.object.all()的查询集。 如果那时想确保预取我可以做:

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

我的问题是:

  1. 有没有办法使用select_related或{pre}执行此预取 我必须使用prefetch_related吗?
  2. 有没有办法预取 page_set?这不起作用:
  3. Publisher.objects.all().prefetch_related('book_set', 'book_set_page_set')

2 个答案:

答案 0 :(得分:27)

  1. 不,您不能将select_related用于反向关系。 select_related执行SQL连接,因此主查询集中的单个记录只需引用相关表(ForeignKeyOneToOne字段中的一个记录)。 prefetch_related实际上做了一个完全独立的第二个查询,缓存结果,然后将它“连接”到python中的查询集中。因此,ManyToMany或反向ForeignKey字段需要它。

  2. 您是否尝试过两个下划线来执行多级预取?像这样:Publisher.objects.all().prefetch_related('book_set', 'book_set__page_set')

答案 1 :(得分:15)

自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来处理预取结果(过滤器/顺序)。