You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django:使用extra()替代annotate()为产品标注近7天销售总重量

解决方案:用extra()为Product标注过去7天销售总重量

嘿,这问题我熟!之前也碰到过多次关联下annotate()重复计数的坑,用extra()通过子查询来实现确实能完美避开这个问题,我来给你一步步拆解:

先明确必要的模型补充

你给出的Order模型没写完,我先补上实现需求必须的字段(如果你的实际字段名不同,对应调整就行):

class Order(models.Model):
    template = models.ForeignKey(Template, on_delete=models.CASCADE)  # 和Template关联
    weight = models.DecimalField(max_digits=10, decimal_places=2)  # 订单重量
    created_at = models.DateTimeField(auto_now_add=True)  # 订单创建时间

核心查询代码

下面就是用extra()实现的查询逻辑,直接给每个Product实例加上total_sales_weight属性:

from django.utils import timezone
from datetime import timedelta

# 计算7天前的时间点
seven_days_ago = timezone.now() - timedelta(days=7)

# 用extra()子查询获取每个Product的销售总重量
products_with_sales = Product.objects.extra(
    select={
        'total_sales_weight': """
            SELECT SUM(o.weight)
            FROM your_app_order o
            JOIN your_app_template t ON o.template_id = t.id
            WHERE t.product_id = your_app_product.id
            AND o.created_at >= %s
        """
    },
    select_params=(seven_days_ago,),  # 传递参数避免SQL注入
)

代码细节说明

  • 为什么选子查询?:如果直接用annotate()关联Template和Order,会因为一个Product对应多个Template、一个Template对应多个Order,产生笛卡尔积导致重复计算,最终SUM的结果会偏大。而子查询是针对每个Product单独计算对应的订单总重量,完全不会有重复计数的问题。
  • 表名替换提示:代码里的your_app要换成你实际的app名称,比如你的app叫store,那表名就是store_order、store_template、store_product。
  • 空值处理:如果某个Product过去7天没有销售记录,total_sales_weight会是None,你可以在后续逻辑里处理成0或者其他默认值。

验证用法示例

拿到查询集后,直接访问属性就能获取数值:

for product in products_with_sales:
    print(f"{product.name}: {product.total_sales_weight or 0} kg")

内容的提问来源于stack exchange,提问作者bdoubleu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:00:15