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
相关产品推荐
相关产品推荐

