具有较少查询的父对象及其子对象列表

leo*_*ews 5 python django query-optimization django-queryset

我有一个Django视图,我正在尝试优化.它显示页面上的父对象列表及其子对象.子模型将外键返回到父项,因此select_related似乎不适用.

class Parent(models.Model):
    name = models.CharField(max_length=31)

class Child(models.Model):
    name = models.CharField(max_length=31)
    parent = models.ForeignKey(Parent)
Run Code Online (Sandbox Code Playgroud)

一个简单的实现使用n + 1个查询,其中n是父对象的数量,即.一个查询用于获取父列表,然后一个查询用于获取每个父项的子项.

我写了一个视图,在两个查询中完成工作 - 一个用于获取父对象,另一个用于获取相关的子对象,然后是一些Python(我太尴尬了,无法在此处发布)将它们全部重新组合在一起.

一旦我发现自己导入标准库的collections模块,我意识到我可能做错了.可能有一个更简单的方法,但我缺乏Django经验来找到它.任何指针将非常感谢!

def*_*fuz 3

related_name在外键中添加a ,然后使用prefetch_relatedDjango 1.4中添加的方法:

返回 ,QuerySet它将在单个批次中自动检索每个指定查找的相关对象。

这与 具有相似的目的select_related,因为两者都旨在阻止因访问相关对象而导致的数据库查询的泛滥,但策略却截然不同:

  • select_related通过创建 SQL 连接并在语句中包含相关对象的字段来工作SELECT。因此, select_related在同一个数据库查询中获取相关对象。然而,为了避免通过“多”关系加入而产生更大的结果集,select_related仅限于单值关系 - 外键和一对一。

  • prefetch_related另一方面,对每个关系进行单独的查找,并在 Python 中进行“连接”。这使得它能够预取多对多和多对一对象,这是使用 无法完成的select_related,此外还有 所支持的外键和一对一关系select_related。它还支持预取GenericRelationGenericForeignKey

class Parent(models.Model):
    name = models.CharField(max_length=31)

class Child(models.Model):
    name = models.CharField(max_length=31)
    parent = models.ForeignKey(Parent, related_name='children') 


>>> Parent.objects.all().prefetch_related('children')
Run Code Online (Sandbox Code Playgroud)

所有相关子项都将在单个查询中获取,并用于创建具有相关结果的预填充缓存的查询集。然后在调用中使用这些查询集self.children.all()

注意 1,与 QuerySet 一样,任何暗示不同数据库查询的后续链接方法将忽略先前缓存的结果,并使用新的数据库查询检索数据。

注意 2,如果您用来iterator()运行查询,prefetch_related()调用将被忽略,因为这两个优化一起没有意义。