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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 04:12:33