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

Django中通过注解一步查询员工关联的预算ID(外键与多对多)

问题解答

原代码的查询次数

你的原代码实际上会执行三次数据库查询,而非两次:

  • 第一次:Employee.objects.get(id=employee_id) 查询员工基础信息
  • 第二次:访问 employee.company 时,因Django外键的懒加载特性,会单独发起查询获取该员工所属公司
  • 第三次:访问 company.budgets.values_list(...) 时,查询该公司关联的所有预算ID

用一次查询实现需求的方案

可以通过聚合注解实现单次查询完成操作,具体实现依赖你使用的数据库类型:

方案1:PostgreSQL 环境(推荐)

利用PostgreSQL支持的ArrayAgg聚合函数,直接将关联的预算ID聚合为数组:

from django.contrib.postgres.aggregates import ArrayAgg

employee = Employee.objects.annotate(
    allowed_budgets=ArrayAgg('company__budgets__id', distinct=True)
).get(id=employee_id)

执行后,employee.allowed_budgets 就是包含所有关联预算ID的列表,整个过程仅触发一次数据库查询——SQL层面会通过JOIN关联Employee、Company、多对多中间表和Budget表,并用聚合函数将符合条件的ID合并为数组。

方案2:非PostgreSQL 环境(如MySQL)

使用StringAgg将ID拼接为字符串,后续再转换为列表:

from django.db.models import StringAgg

employee = Employee.objects.annotate(
    allowed_budgets_str=StringAgg('company__budgets__id', delimiter=',', distinct=True)
).get(id=employee_id)
# 将拼接字符串转为整数列表
allowed_budgets = list(map(int, employee.allowed_budgets_str.split(','))) if employee.allowed_budgets_str else []
备选方案:减少查询次数至两次

如果不需要将预算ID作为员工对象的注解字段,也可以用预取关联的方式优化:

employee = Employee.objects.select_related('company').prefetch_related('company__budgets').get(id=employee_id)
allowed_budgets = employee.company.budgets.values_list('id', flat=True)

这种方式下,第一次查询通过JOIN获取员工和关联公司,第二次批量获取所有需要的预算数据,比原代码的三次查询更高效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 20:35:16