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

如何为Django QuerySet添加子查询统计的职位数量注解?

解决Django中Location层级职位统计的问题

问题背景

构建求职板时,Location模型可表示国家、州、城市三级层级,通过slug的后缀关联父级区域(如州的slug为new-york-united-states,包含国家后缀)。需要为每个Location注解number_of_jobs,统计该区域及下属所有区域的关联职位总数(如纽约州需统计其下所有城市的职位)。

正确解决方案

使用Subquery结合虚拟分组的方式,避免Django生成不必要的GROUP BY,从而统计出正确的总数:

from django.db.models import Subquery, OuterRef, Count, Value, CharField

# 构建子查询:通过虚拟字段分组,统计符合条件的distinct Job总数
job_count_subquery = Job.objects.filter(
    locations__slug__endswith=OuterRef("slug")
).distinct().annotate(
    # 添加虚拟字段,用于将所有结果归为一组
    dummy=Value("", output_field=CharField())
).values("dummy").annotate(
    total_jobs=Count("id")
).values("total_jobs")[:1]

# 为每个Location注解职位总数
location_with_job_counts = Location.objects.annotate(
    number_of_jobs=Subquery(job_count_subquery)
)

此方案会生成正确的统计结果:

  • 曼哈顿:1个职位
  • 布鲁克林:1个职位
  • 纽约州:2个职位
  • 美国:2个职位

各尝试失败原因解析

尝试1:直接使用Count("jobs")

Location.objects.annotate(number_of_jobs=Count("jobs", distinct=True))

失败原因:Count("jobs")仅统计直接关联到当前Location的Job数量。州/国家层级的Location没有直接关联的Job(Job都关联到城市层级Location),因此统计结果为0。

尝试2:使用count()作为Subquery

subquery = Job.objects.filter(locations__slug__endswith=OuterRef("slug")).distinct().count()
Location.objects.annotate(number_of_jobs=Subquery(subquery))

失败原因:count()是立即执行的查询方法,会直接返回数据库查询的数值,而非查询集。Subquery要求传入的是未执行的查询集,因此此写法会报错。

尝试3:annotate导致不必要的GROUP BY

jobs = Job.objects.filter(locations__slug__endswith=OuterRef("slug"))
subquery = jobs.annotate(job_count=Count("id", distinct=True)).values("job_count")[:1]
Location.objects.annotate(number_of_jobs=Subquery(subquery))

失败原因:Django中,对模型QuerySet调用annotate()时,默认会按模型的主键字段(此处为Job.id)进行分组,目的是为每个Job对象添加注解字段。这会导致SQL中生成GROUP BY job.id,子查询返回的是单个Job的count值(恒为1),而非符合条件的Job总数。

Django自动添加GROUP BY的原因

当你对模型QuerySet使用annotate()时,Django的ORM逻辑是:为QuerySet中的每个对象生成注解字段。为了实现这一点,ORM会自动按模型的主键进行分组,确保每个对象对应一条注解结果。如果你的需求是对整个QuerySet进行聚合统计(而非每个对象),就需要通过虚拟分组(如上述方案中的dummy字段)来覆盖默认的分组行为。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 15:09:57