【问题标题】:Django3 prefetch_relatedDjango3 prefetch_related
【发布时间】:2020-07-01 08:42:32
【问题描述】:

我曾在 django 2.X 工作过。但我将在我的新项目中使用 django3.x。 在版本 2 中,我应该进行外部连接。我使用了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 by Member 的权限),它不能。

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

【问题讨论】:

  • 您是如何确定这是版本差异造成的?您是否创建了一个计算查询数量的测试,然后在 2 个不同的虚拟环境中运行它(使用 [tox](例如tox.readthedocs.io/en/latest)来显示问题?
  • @Melvyn 对不起,我犯了错误。我已经通过 venvs 再次尝试作为您的评论。它被执行为相同没有不同。如果我使用 '''prefetch_related''' 并尝试获取列,总是执行多个查询?

标签: django django-3.0


【解决方案1】:

这不是版本差异。这就是prefetch_related 的工作方式。它将为每个外连接执行 1 个额外的查询。但是,这仍然比每次迭代执行 1 个查询要少 很多documentation 对此非常清楚:

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

另一方面,prefetch_related 对每个关系进行单独的查找,并在 Python 中进行“连接”。除了 select_related 支持的外键和一对一关系之外,这允许它预取多对多和多对一对象,这是使用 select_related 无法完成的。

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

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

因此,担心每次连接多出 1 个查询是没有意义的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-28
    • 2012-10-07
    • 1970-01-01
    • 2021-04-17
    • 1970-01-01
    • 2021-06-23
    • 2021-05-23
    相关资源
    最近更新 更多