如何在Django中从DateField提取年份并结合ChartJS展示年度聚类柱状图
没问题,我帮你搞定这个需求,从Django后端按年份聚合数据,到前端用Chart.js渲染聚类柱状图,一步一步来:
1. 后端:按年份统计金额总和
首先我们需要从Paymentdete表中提取年份,并对三个金额字段做年度求和。Django的ORM提供了ExtractYear和Sum工具可以轻松实现这个需求。
步骤1:导入必要的模块
在你的视图文件里导入以下内容:
from django.db.models import Sum, ExtractYear from .models import Paymentdete from django.http import JsonResponse
步骤2:编写聚合查询
用ORM分组统计每个年份的总金额、已收金额和剩余金额:
def yearly_payment_stats(request): # 过滤掉日期为空的记录,避免无效数据 yearly_data = Paymentdete.objects.filter(date__isnull=False).values( # 从date字段提取年份作为分组键 year=ExtractYear('date') ).annotate( # 对三个金额字段分别求和 total_amount_sum=Sum('total_amount'), amount_received_sum=Sum('amountreceived'), amount_left_sum=Sum('amount_left') ).order_by('year') # 按年份升序排列 # 将数据整理成前端容易处理的格式 labels = [item['year'] for item in yearly_data] total_amounts = [item['total_amount_sum'] for item in yearly_data] received_amounts = [item['amount_received_sum'] for item in yearly_data] left_amounts = [item['amount_left_sum'] for item in yearly_data] # 返回JSON格式数据给前端 return JsonResponse({ 'labels': labels, 'total_amount': total_amounts, 'amount_received': received_amounts, 'amount_left': left_amounts })
步骤3:配置URL
在你的应用URL配置里添加这个视图的路由:
from django.urls import path from . import views urlpatterns = [ # 其他路由... path('payment-stats/', views.yearly_payment_stats, name='payment_stats'), ]
2. 前端:用Chart.js渲染聚类柱状图
接下来我们在模板里用Chart.js把后端返回的数据渲染成聚类柱状图。
步骤1:引入Chart.js
建议把Chart.js下载到项目的静态文件目录(比如static/js/),然后在模板里引入:
{% load static %} <script src="{% static 'js/chart.min.js' %}"></script>
步骤2:添加Canvas容器
在模板里创建一个用于渲染图表的Canvas元素:
<div style="width: 90%; margin: 2rem auto;"> <canvas id="yearlyPaymentChart"></canvas> </div>
步骤3:编写JavaScript渲染图表
用fetch获取后端数据,然后初始化Chart.js实例:
document.addEventListener('DOMContentLoaded', function() { // 调用后端接口获取统计数据 fetch('/payment-stats/') // 替换成你实际的URL路径 .then(response => response.json()) .then(data => { const chartCtx = document.getElementById('yearlyPaymentChart').getContext('2d'); new Chart(chartCtx, { type: 'bar', // 指定为柱状图类型 data: { labels: data.labels, // 年份作为X轴标签 datasets: [ { label: '总金额', data: data.total_amount, backgroundColor: 'rgba(54, 162, 235, 0.6)', borderColor: 'rgba(54, 162, 235, 1)', borderWidth: 1 }, { label: '已收金额', data: data.amount_received, backgroundColor: 'rgba(75, 192, 192, 0.6)', borderColor: 'rgba(75, 192, 192, 1)', borderWidth: 1 }, { label: '剩余金额', data: data.amount_left, backgroundColor: 'rgba(255, 99, 132, 0.6)', borderColor: 'rgba(255, 99, 132, 1)', borderWidth: 1 } ] }, options: { responsive: true, scales: { y: { beginAtZero: true, // Y轴从0开始 title: { display: true, text: '金额' } }, x: { title: { display: true, text: '年份' } } }, plugins: { title: { display: true, text: '年度付款金额统计' } } } }); }) .catch(error => console.error('获取数据失败:', error)); });
一些额外注意事项
- 如果你的
date字段存在null值,ExtractYear会返回null,所以我们在查询里加了filter(date__isnull=False)来过滤无效数据。 - 如果你需要展示连续的年份(哪怕某一年没有数据),可以在后端补全年份列表,把对应金额设为0。
- 确保你的Django版本支持
ExtractYear(Django 1.10及以上版本都支持)。
内容的提问来源于stack exchange,提问作者Dhruvil Shah
相关产品推荐
相关产品推荐

