多级预取(prefetch_related)的应用方法

55
如果我的模型看起来像这样:

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)

我想获取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')

2个回答

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

因为我使用 ListQuerySet 处理预取结果(过滤/排序),所以它被存储在带有 _list 后缀的属性中。


7
这个回答非常出色,可以帮助您节省大量的数据库流量。我不明白为什么这个回答没有得到太多关注。 - Mohammed Shareef C
嗨@DmitriySintsov,我能请你帮个忙吗:-https://stackoverflow.com/questions/68682472/api-endpoint-for-django-group-model-generating-excess-number-of-queries - Manish Shah
7
这里的解决方案使用了未知的模型和未知的结构。如果您使用OP提供的模型,或者解释一下您项目示例的结构,那么理解起来就会容易得多。如果我们得到了这样的清晰度,很高兴撤销-1。 - dKen

65
  1. 不可以使用 select_related 来获取反向关系的数据。因为 select_related 执行的是 SQL 连接操作,主查询集中的一条记录需要恰好引用相关表中的一条记录 (ForeignKey 或者 OneToOne 字段)。而对于 ManyToMany 或反向的 ForeignKey 字段,则需要使用 prefetch_related。后者会执行完全独立的第二个查询,并将结果缓存起来,然后在 Python 中执行“连接”操作将其合并到查询集中。

  2. 你尝试过使用双下划线进行多级预取吗?像这样:Publisher.objects.all().prefetch_related('book_set', 'book_set__page_set')


1
如果Page有一个指向TextContentOneToOne字段,应该是:...prefetch_related('book_set__page_set__text_contents')还是...select_related('book_set__page_set__text_contents') - Alex Rothberg
1
我相信这将是第二个版本。 - jproffitt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接