如何用Django ORM实现两个关联同一第三表的模型关联查询?
Django ORM 实现多表关联查询(左/右/内连接)
首先假设你的模型结构如下(若与实际结构有出入,调整字段名即可):
from django.db import models class MaterialList(models.Model): name = models.CharField(max_length=100) # 其他物料属性字段 class CountSchedule(models.Model): material = models.ForeignKey(MaterialList, on_delete=models.CASCADE, related_name='schedules') count_date = models.DateField() scheduled_quantity = models.IntegerField() # 其他计划计数字段 class ActualCounts(models.Model): material = models.ForeignKey(MaterialList, on_delete=models.CASCADE, related_name='actuals') count_date = models.DateField() actual_quantity = models.IntegerField() # 其他实际计数字段
1. 内连接(仅返回双向匹配的记录)
内连接会返回同时存在于CountSchedule和ActualCounts中,且count_date与material完全匹配的记录。
可以通过SubQuery关联匹配数据,再过滤掉无匹配的记录:
from django.db.models import Subquery, OuterRef # 子查询获取对应实际计数 actual_subquery = ActualCounts.objects.filter( material=OuterRef('material'), count_date=OuterRef('count_date') ).values('actual_quantity')[:1] inner_join_result = CountSchedule.objects.annotate( actual_quantity=Subquery(actual_subquery) ).filter(actual_quantity__isnull=False).values( 'material__name', 'count_date', 'scheduled_quantity', 'actual_quantity' )
2. 左连接(返回所有计划记录,无匹配则实际字段为Null)
左连接以CountSchedule为主表,返回所有计划记录,即使没有对应的实际计数记录:
from django.db.models import Subquery, OuterRef actual_subquery = ActualCounts.objects.filter( material=OuterRef('material'), count_date=OuterRef('count_date') ).values('actual_quantity')[:1] left_join_result = CountSchedule.objects.annotate( actual_quantity=Subquery(actual_subquery) ).values( 'material__name', 'count_date', 'scheduled_quantity', 'actual_quantity' )
无匹配的实际记录会以None填充actual_quantity字段,完全对应左连接效果。
3. 右连接(返回所有实际记录,无匹配则计划字段为Null)
Django ORM无直接右连接语法,可通过调换主表模拟——以ActualCounts为主表,关联匹配的计划记录:
from django.db.models import Subquery, OuterRef schedule_subquery = CountSchedule.objects.filter( material=OuterRef('material'), count_date=OuterRef('count_date') ).values('scheduled_quantity')[:1] right_join_result = ActualCounts.objects.annotate( scheduled_quantity=Subquery(schedule_subquery) ).values( 'material__name', 'count_date', 'scheduled_quantity', 'actual_quantity' )
无对应计划的记录会以None填充scheduled_quantity字段,等价于右连接。
注意事项
- 若同一
material+count_date组合存在多条重复记录,需根据业务需求调整子查询逻辑(比如用Sum聚合多条记录,或指定排序规则取特定条)。 - 使用
values()直接提取报表所需字段,避免加载完整模型实例,提升查询效率。 - 如需复杂统计(求和、平均值等),可结合
Sum/Avg等聚合函数使用。
内容的提问来源于stack exchange,提问作者Calvin C
相关产品推荐
相关产品推荐

