鸡 prefetch_related

Django3 prefetch_related

我曾在 django 2.X 工作过。但我将在我的新项目中使用 django3.x。 在 version2,当我应该进行外部连接时。我使用 prefetch_related 并过滤了 prefetch_related 的模型。

在版本 2 中,如果我使用 prefetch_related,它会作为单个查询进行查询。但在版本 3 中,通过多个查询进行查询。

如果我只使用连接目标的 Q() 而没有 prefetch_related,它可以在版本 3 中使用单个查询。

from django.db import models
from django.db.models import Q
from django.db.models import Prefetch


class Member(models.Model):
    member_no = models.AutoField()
    member_name = models.CharField()


class Permission(models.Model):
    permission_no = models.AutoField()


class MemberPermission(models.Model):
    member_permission_no = models.AutoField()
    member_no = models.ForeignKey(
        Member, related_name='members', on_delete=models.CASCADE,
    )
    permission_no = models.ForeignKey(
        Permission, related_name='member_permissions', on_delete=models.CASCADE,
    )


my_permission = Member.objects.prefetch_related('member_permissions').filter(Q(member_permissions__isnull=False))[:1]
print(my_permission[0].member_permissions)  
# member outer join permission, single query at django 2.X
# member outer join permission & additional query at django 3.x

my_permission = Member.objects.filter(Q(member_permissions__isnull=False))[:1]
print(my_permission[0].member_permissions)  
# member outer join permission, single query at django 3.X

my_permission = Member.objects.prefetch_related(
                    Prefetch('member_permissions', MemberPermission.objects.select_related(
                        'permission_no').all())
                ).filter(Q(members__isnull=False))[:1]
print(my_permission[0].member_permissions.all()[0].permission_no.permission_no)
# member outer join permission & additional query at django 3.x

如果我不使用 prefetch_related,我可以获得单个查询。

但是如果我想获取加入模型的模型(MemberPermission的权限)它不能。

我想知道如何在django3中通过Prefetch()查询一次

这不是版本差异。这就是 prefetch_related 的工作方式。它将为每个外部连接执行 1 个额外的查询。但是,这仍然比每次迭代执行 1 个查询少 lotdocumentation 说的很清楚了:

select_related works by creating an SQL join and including the fields of the related object in the SELECT statement. For this reason, select_related gets the related objects in the same database query. However, to avoid the much larger result set that would result from joining across a ‘many’ relationship, select_related is limited to single-valued relationships - foreign key and one-to-one.

prefetch_related, on the other hand, does a separate lookup for each relationship, and does the ‘joining’ in Python. This allows it to prefetch many-to-many and many-to-one objects, which cannot be done using select_related, in addition to the foreign key and one-to-one relationships that are supported by select_related.

假设我们有 2 个外连接,总共有 1000 个匹配行:

  • 没有prefetch_related的查询数:1 + 2*1000 = 2001
  • prefetch_related 的查询数量:1 + 2 = 3

因此,不必担心每个连接的 1 个额外查询。