首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Django:使用扩展数据优化查询

Django:使用扩展数据优化查询
EN

Stack Overflow用户
提问于 2019-02-22 10:55:18
回答 1查看 452关注 0票数 1

我有Order对象和OrderOperation对象,它们表示订单上的操作(创建、修改、取消)。

从概念上讲,一个订单有1到多个订单操作。每次对订单进行操作时,都会在此操作中计算总数。这意味着当我需要找到一个订单的总数时,我只得到最后一个订单操作总数。

简化代码

代码语言:javascript
复制
class OrderOperation(models.Model):
    order = models.ForeignKey(Order)
    total = DecimalField(max_digits=9, decimal_places=2)

class Order(models.Model):

    @property
    def last_operation(self) -> Optional['OrderOperation']:
        try:
            qs = self.orderoperation_set.all()
            return qs[len(qs) - 1]
        except AssertionError:  # when there is a negative indexing (no operation)
            # IndexError can not happen
            return None

    @property
    def total(self) -> Optional[Decimal]:
        last_operation = self.last_operation
        return last_operation.total if last_operation else None

The issue

由于我收到了大量订单,每次我想进行简单的筛选,比如“总低于5欧元的订单”,这需要很长的时间,因为我需要浏览所有订单,使用以下明显错误的查询:

代码语言:javascript
复制
all_objects = Order.objects.all()
Order.objects.prefetch_related('orderoperation_set').filter(
    pk__in=[o.pk for o in all_objects if o.total <= some_value])

我现在的想法/我尝试过的

数据去正规化?

我可以简单地在total上创建一个Order属性,并在每次创建操作时将操作总数复制到订单总数中。然后,Order.objects.filter(total__lte=some_value)就能工作了。但是,在复制数据库中的数据之前,我希望确保没有更简单/更干净的解决方案。

使用注释()方法的

不知何故,我希望能够做到:Order.objects.annotate(total=something_magical_here).filter(total__lte=some_value)。似乎是不可能的。

滤波,然后进行匹配?

代码语言:javascript
复制
order_operations = OrderOperation.objects.filter(total__lte=some_value)
orders = Order.objects.filter(orderoperation__in=order_operations)

这是非常快,但过滤是不好的,因为我没有过滤最后的操作,但这里的所有操作。这是错误的。

还有别的主意吗?谢谢。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-02-22 13:16:49

使用注释()方法

似乎是不可能的。

当然,这是可能的;)您可以使用子查询或一些聪明的条件表达式。假设您希望从上一次订单操作中获得总量,下面是带有子查询的示例:

代码语言:javascript
复制
from django.db.models import Subquery, OuterRef

orders = Order.objects.annotate(
    total=Subquery(                             # [1]
        OrderOperation.objects \
            .filter(order_id=OuterRef("pk")) \  # [2]
            .order_by('-id') \                  # [3]
            .values('total') \                  # [4]
            [:1]                                # [5]
    )
)

对上述守则的解释:

  1. 我们正在向结果列表中添加新字段,称为total taht将由子查询填充。您可以像在这个查询集中访问模型Order的任何其他字段一样访问它(在对其进行评估之后、在模型实例中或在过滤和其他注释中)。您可以从姜戈博士了解注释是如何工作的。
  2. 子查询只应针对当前订单中的操作调用。OuterRef将被替换为在结果查询中引用所选字段。
  3. 我们希望通过id降序操作订购,因为我们确实需要最新的订单。如果您的操作中有其他字段,您希望通过它来订购(比如创建日期),请在这里填写。
  4. 该子查询只应从操作中返回total值。
  5. 我们只想要一个元素。它是使用片表示法而不是普通索引来获取的,因为在django queryset上使用索引将立即调用它。切片只将LIMIT子句添加到SQL查询中,而不调用它,这就是我们想要的。

现在您可以使用:

代码语言:javascript
复制
orders.filter(total__lte=some_value)

只取你想要的订单。您还可以使用该注释来

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54825556

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档