gpt4 book ai didi

python - Django:使用传播数据优化查询

转载 作者:行者123 更新时间:2023-12-01 08:18:39 24 4
gpt4 key购买 nike

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

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

简化代码

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

问题

由于我收到很多订单,每次我想做一个简单的过滤,比如“总金额低于5欧元的订单”,需要很长时间,因为我需要浏览所有订单,使用以下命令,明显错误的查询:

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])

我目前的想法/我尝试过的

数据非规范化?

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

使用 annotate() 方法?

我不知何故希望能够做到:Order.objects.annotate(total=something_magical_here).filter(total__lte=some_value)。看来不可能了。

单独过滤然后匹配?

order_operations = OrderOperation.objects.filter(total__lte=some_value)
orders = Order.objects.filter(orderoperation__in=order_operations)

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

还有其他想法吗?谢谢。

最佳答案

使用annotate()方法

It seems it's not possible.

当然,这是可能的;)您可以使用子查询或一些巧妙的条件表达式。假设您想获取上次订单操作的总金额,以下是子查询的示例:

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. 我们正在向结果列表中添加新字段,称为“总计”,该字段将由子查询填充。您可以像此查询集中的模型 Order 的任何其他字段一样访问它(在评估它之后、在模型实例中或在过滤和其他注释中)。您可以从Django docs了解注释的工作原理.
  2. 子查询只能针对当前订单的操作调用。 OuterRef 将被替换为对结果 SQL 查询中选定字段的引用。
  3. 我们希望按操作 id 降序排序,因为我们确实需要最新的操作。如果您希望按照操作中的其他字段进行排序(例如创建日期),请在此处填写。
  4. 该子查询应该只返回操作的total
  5. 我们只需要一个元素。它是使用切片符号而不是普通索引来获取的,因为在 django 查询集上使用索引将立即调用它。切片仅向 SQL 查询添加 LIMIT 子句,而不调用它,这就是我们想要的。

现在您可以使用:

orders.filter(total__lte=some_value)

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

关于python - Django:使用传播数据优化查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54825556/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com