如何基于Django的AppealForm模型查询数据适配ChartJS折线图?
Django按自然月统计AppealForm关联Appeal数量(含空月份补0)
核心思路
要适配ChartJS的折线图需求,需要确保12个自然月的数据完整性——即使某月份没有关联的Appeal记录,也要返回0值。实现分两步:先生成全年12个月的基准框架,再将实际统计数据填充到框架中,空缺月份自动补0。
假设模型关系(需根据实际字段调整)
先明确模型关联逻辑(如果你的字段名不同,后续代码对应修改即可):
Appeal模型通过外键(比如appeal_form)关联到AppealFormAppeal包含时间字段(比如created_at)用于按月份统计AppealForm包含标识字段(比如name)用于区分不同表单
具体实现代码
1. 导入依赖模块
from django.db.models import Count from django.db.models.functions import TruncMonth from datetime import datetime
2. 生成全年12个月的基准数据
先构建一个包含当年所有月份的基础结构,确保不会缺失任何月份:
# 可替换为指定年份,比如target_year = 2024 current_year = datetime.now().year # 生成格式为"YYYY-MM-01"的月份基准列表 month_baseline = [ {"month_str": f"{current_year}-{month:02d}-01"} for month in range(1, 13) ]
3. 执行分组统计查询
通过TruncMonth按月份截断时间字段,结合Count统计每个AppealForm各月份的关联Appeal数量:
# 统计当前年份内,每个AppealForm各月份的Appeal数量 appeal_monthly_stats = AppealForm.objects.annotate( # 截断Appeal的创建时间到月份,注意替换为实际字段路径 month=TruncMonth('appeal__created_at') ).values('id', 'name', 'month').annotate( appeal_count=Count('appeal') ).filter( month__year=current_year ).order_by('id', 'month')
4. 补全空月份数据
将查询结果与基准月份合并,空缺月份自动填充0:
# 整理为每个AppealForm对应完整12个月的统计结果 form_monthly_data = {} for stat in appeal_monthly_stats: form_id = stat['id'] form_name = stat['name'] # 将日期对象转为基准列表的字符串格式 stat_month_str = stat['month'].strftime('%Y-%m-%d') count = stat['appeal_count'] if form_id not in form_monthly_data: # 初始化该表单的12个月数据,默认值为0 form_monthly_data[form_id] = { 'form_id': form_id, 'form_name': form_name, 'counts': {mb['month_str']: 0 for mb in month_baseline} } # 填充有数据的月份 form_monthly_data[form_id]['counts'][stat_month_str] = count # 转换为列表格式,方便传递给前端 final_result = list(form_monthly_data.values())
5. 适配ChartJS数据格式
将处理后的数据转换为ChartJS所需的标签+数据数组结构:
# 生成x轴的月份标签(比如"Jan", "Feb") month_labels = [ datetime.strptime(mb['month_str'], '%Y-%m-%d').strftime('%b') for mb in month_baseline ] # 生成折线图的数据集 chart_datasets = [] for form_data in final_result: chart_datasets.append({ 'label': form_data['form_name'], 'data': list(form_data['counts'].values()), 'borderColor': f'#{hash(form_data["form_name"]) % 0xFFFFFF:06x}', # 动态生成唯一颜色 'fill': False, 'tension': 0.1 })
额外调整说明
- 若需区分
written/oral类型的Appeal,可在查询时添加过滤条件:.filter(appeal__type='written'),分别统计两类数据后合并到ChartJS的数据集。 - 如果模型的外键字段名或时间字段名不同,比如
Appeal关联AppealForm的外键是form,时间字段是submitted_at,则修改TruncMonth('form__submitted_at')即可。
内容的提问来源于stack exchange,提问作者Firdavsbek Narzullaev
相关产品推荐
相关产品推荐

