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

如何基于共同字段month合并两个Django QuerySet

问题描述

我有两个Django QuerySet,均包含month字段,分别用于统计过去6个月的每月新患者注册数和每月就诊数。现有代码如下:

six_months_ago = date.today() + relativedelta(months=-6)
###### 获取过去6个月每月新患者注册数
patient_per_month = patientprofile.objects.filter(pat_Registration_Date__gte=six_months_ago)\
.annotate(
   month=Trunc('pat_Registration_Date', 'month')
) \
.order_by('month') \
.values('month') \
.annotate(patient=Count('month'))
print(patient_per_month)

###### 获取过去6个月每月就诊数
visits_per_month = visits.objects.filter(visit_date__gte=six_months_ago)\
.annotate(
   month=Trunc('visit_date', 'month')
) \
.order_by('month') \
.values('month') \
.annotate(visits=Count('month'))

print(visits_per_month)

两个QuerySet输出结果:

<QuerySet [{'month': datetime.date(2022, 2, 1), 'patient': 1}, {'month': datetime.date(2022, 3, 1), 'patient': 1}, {'month': datetime.date(2022, 4, 1), 'patient': 1}, {'month': datetime.date(2022, 5, 1), 'patient': 2}, {'month': datetime.date(2022, 6, 1), 'patient': 2}, {'month': datetime.date(2022, 7, 1), 'patient': 18}]>

<QuerySet [{'month': datetime.date(2022, 5, 1), 'visits': 6}, {'month': datetime.date(2022, 6, 1), 'visits': 7}, {'month': datetime.date(2022, 7, 1), 'visits': 12}]>

需要基于month字段合并两个QuerySet,缺失的数值以0填充,得到如下格式的结果:

<QuerySet [{'month': datetime.date(2022, 2, 1), 'patient': 1, 'visits': 0}, {'month': datetime.date(2022, 3, 1), 'patient': 1, 'visits': 0}, {'month': datetime.date(2022, 4, 1), 'patient': 1, 'visits': 0}, {'month': datetime.date(2022, 5, 1), 'patient': 2, 'visits': 6}, {'month': datetime.date(2022, 6, 1), 'patient': 2, 'visits': 7}, {'month': datetime.date(2022, 7, 1), 'patient': 18, 'visits': 12}]>

解决方案

方法一:Python层面合并(简单易实现)

将两个QuerySet转换为字典,以month为键存储统计值,再遍历所有存在的月份生成合并数据:

# 转换QuerySet为字典,快速查找对应月份的统计值
patient_map = {item['month']: item['patient'] for item in patient_per_month}
visits_map = {item['month']: item['visits'] for item in visits_per_month}

# 获取所有涉及的月份并排序
all_months = sorted(set(patient_map.keys()).union(visits_map.keys()))

# 生成合并后的结果列表
merged_result = []
for month in all_months:
    merged_result.append({
        'month': month,
        'patient': patient_map.get(month, 0),
        'visits': visits_map.get(month, 0)
    })

如果需要严格覆盖过去6个月的所有月份(即使当月无数据),可以先生成完整的月份列表:

from datetime import date
from dateutil.relativedelta import relativedelta

# 生成过去6个月的每月1号日期
current_month = date.today().replace(day=1)
all_months = []
for i in range(6):
    all_months.append(current_month - relativedelta(months=i))
all_months.sort()

# 生成合并数据
merged_result = []
for month in all_months:
    merged_result.append({
        'month': month,
        'patient': patient_map.get(month, 0),
        'visits': visits_map.get(month, 0)
    })

方法二:数据库层面合并(大数据量更高效)

通过Django的Subquery和Coalesce在数据库层完成合并,减少内存占用:

from django.db.models import OuterRef, Subquery, IntegerField
from django.db.models.functions import Coalesce

# 定义就诊数的子查询
visits_subquery = visits.objects.filter(
    visit_date__gte=six_months_ago,
    month=OuterRef('month')
).annotate(
    month=Trunc('visit_date', 'month')
).values('month').annotate(visits_count=Count('month')).values('visits_count')[:1]

# 合并患者统计与就诊统计,用Coalesce将NULL替换为0
merged_queryset = patientprofile.objects.filter(pat_Registration_Date__gte=six_months_ago)\
.annotate(
    month=Trunc('pat_Registration_Date', 'month')
).order_by('month')\
.values('month')\
.annotate(
    patient=Count('month'),
    visits=Subquery(visits_subquery, output_field=IntegerField())
).annotate(
    visits=Coalesce('visits', 0)
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 18:39:28