为了账号安全,请及时绑定邮箱和手机立即绑定

Django:prefetch_related无效

Django:prefetch_related无效

三国纷争 2021-04-28 21:01:33
我正在尝试优化数据库查询,prefetch_related但没有成功。models.pyclass Order(models.Model):    # some fields ...    @property    def last_operation(self) -> Optional['OrderOperation']:        try:            return self.orderoperation_set.latest()        except OrderOperation.DoesNotExist:            return None    @property    def total(self) -> Optional[Decimal]:        last_operation = self.last_operation        return last_operation.total if last_operation else Noneclass OrderOperation(TimeStampable, models.Model):    order = models.ForeignKey(Order)    total = DecimalField(max_digits=9, decimal_places=2)运行一个shell,我可以看到问题所在:orders = Order.objects.prefetch_related('orderoperation_set')  # There are 1000 ordersresult = sum([order.total for order in orders])len(connection.queries)>>> 1003我们可以看到,每个查询有一个查询order.total,因此有1000个查询,这使整个请求非常糟糕,性能与订单数成线性关系。为了理解为什么会这样,我在prefetch_related Django doc中找到了这个:请记住,与QuerySet一样,任何暗示不同数据库查询的后续链接方法都将忽略先前缓存的结果,并使用新的数据库查询来检索数据。因此,latest()每次调用都运行一个新查询似乎很正常。在这种情况下,您将如何提高性能?(而不是N进行一些查询,其中N是订单数)。
查看完整描述

2 回答

?
慕尼黑8549860

TA贡献1818条经验 获得超11个赞

由于OrderOperation仅包含单个相关字段,因此total,更好的方法是使用子查询来注释原始查询中最新操作的总和:


from django.db.models import OuterRef, Subquery

newest = OrderOperation.objects.filter(post=OuterRef('pk')).order_by('-created_at')  # or whatever the timestamp field is

orders = Order.objects.annotate(newest_operation_total=Subquery(newest.values('total')[:1]))



查看完整回答
反对 回复 2021-05-11
?
手掌心

TA贡献1942条经验 获得超3个赞

我在这里发布答案,你能告诉我这是否有意义吗?


而不是调用的latest(),如果我只是得到我的查询集与第一项[0](或最后用len(qs)-1,假设order_operations已经订购?


@property

def last_operation(self) -> Optional['OrderOperation']:

    try:

        qs = self.orderoperation_set.all()

        return qs[len(qs) - 1]

    except IndexError:

        return None


查看完整回答
反对 回复 2021-05-11
  • 2 回答
  • 0 关注
  • 249 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号