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

Django 3中如何搭配_in过滤器使用OuterRef实现关联聚合注解

问题场景
  • 业务中存在两个核心模型,分别为存储产品库存的Product、存储订单项购买信息的OrderLine,模型定义如下:
class Product(models.Model):
    inventory_quantity = models.IntegerField()

class OrderLine(models.Model):
    product_id = models.ForeignKey(Product, on_delete=models.CASCADE)
    quantity = models.IntegerField()
  • 需求为给Product查询集添加注解,计算每个产品的预测库存值(现有库存减去所有关联订单项的购买数量总和),原有Django 2版本可用的ORM写法升级到Django 3后存在兼容问题无法运行。
兼容Django 3+的实现方案

Django 3版本调整了子查询中字段引用的校验逻辑,不再支持在子查询内反向引用外层关联模型的字段做计算,直接在外层查询完成库存扣减计算即可,写法更简洁且查询效率更高。

实现关联订单项总数量注解

先通过子查询聚合每个产品关联的所有订单项的购买数量总和,注意给无关联订单项的产品设置默认值0,避免计算结果为空:

from django.db.models import Sum, OuterRef, Subquery, IntegerField, F

# 聚合单产品关联订单项总购买量
order_total_subquery = OrderLine.objects.filter(
    product_id=OuterRef('pk')
).values('product_id')
 .annotate(total_qty=Sum('quantity'))
 .values('total_qty')[:1]

计算预测库存注解

将子查询的聚合结果挂载到Product查询集上,直接通过现有库存字段减去总销量得到预测库存:

products_queryset = Product.objects.annotate(
    # 注解关联订单项总购买量,无订单项时默认取0
    total_order_quantity=Subquery(
        order_total_subquery,
        output_field=IntegerField(),
        default=0
    ),
    # 计算预测库存
    nb_projected=F('inventory_quantity') - F('total_order_quantity')
)

如果不需要单独保留总购买量的注解字段,也可以将逻辑合并简化为单注解写法:

products_queryset = Product.objects.annotate(
    nb_projected=F('inventory_quantity') - Subquery(
        OrderLine.objects.filter(product_id=OuterRef('pk'))
        .values('product_id')
        .annotate(total_qty=Sum('quantity'))
        .values('total_qty')[:1],
        output_field=IntegerField(),
        default=0
    )
)

写法说明

  • 子查询末尾加[:1]是为了确保子查询只返回单个聚合值,符合Subquery表达式的入参要求
  • default=0参数会自动处理没有任何关联订单项的产品场景,避免子查询返回NULL导致库存计算结果为空
  • 该写法生成的SQL仅对订单项表做一次聚合查询,不需要额外关联产品表,查询性能比Django 2版本的旧写法更优

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 04:45:38