Chart.js与Django 3:费用数据月份标签匹配异常问题求助
问题分析与解决方案
这问题我之前也碰到过,核心是你的数据顺序和Chart.js的月份标签顺序没有对应,另外代码里还有个小bug会导致费用统计不准确,我帮你一步步梳理:
问题根源
- 数据与标签不匹配:你当前的
total_earn_month、total_etp_month、total_ep_month都是只包含有数据的月份的字典,直接取values()得到的是仅存在数据的月份值,而你的labels是固定的12个月份顺序。当只有8月的数据时,values()返回的列表只有一个元素,Chart.js会自动把它匹配到第一个标签(January)上,这就是为什么显示成1月数据。 - 待支付费用统计bug:在统计待支付费用的循环里,你写的是
total_etp_month[etp_month] = expense.price,不管是否已经存在该月份的键,都直接赋值而不是累加,这会导致同一个月份的多个待支付费用只会保留最后一个的价格,而不是总和。
修复步骤
我们需要确保每个月份(1-12)都有对应的数据(没有数据的月份填0),这样数据的顺序就能和labels的12个月份完全对应:
1. 初始化全月份的统计字典
把原来的空字典初始化改为包含1-12月、初始值为0的字典,这样每个月份都有初始值,后续只需要累加即可:
# 按月份统计总营收(初始化1-12月为0) total_earn_month = {month: 0 for month in range(1, 13)} for invoice in invoices: invoice_month = invoice.created_at.month total_earn_month[invoice_month] += invoice.total # 按月份统计待支付总费用(修复累加bug+初始化全月份) total_etp_month = {month: 0 for month in range(1, 13)} for expense in expenses_to_pay: etp_month = expense.invoice_date.month total_etp_month[etp_month] += expense.price # 这里改成+=,修复累加bug # 按月份统计已支付总费用(初始化全月份) total_ep_month = {month: 0 for month in range(1, 13)} for expense in expenses_paid: ep_month = expense.purchase_date.month total_ep_month[ep_month] += expense.price
2. 计算总费用时保持全月份顺序
现在因为total_etp_month和total_ep_month都是包含1-12月的字典,直接按月份顺序相加即可,不需要用集合取键:
total_expenses = {month: total_ep_month[month] + total_etp_month[month] for month in range(1, 13)}
3. 返回数据时保持顺序
现在total_earn_month.values()和total_expenses.values()都是按1-12月的顺序返回的,和你的labels顺序完全匹配,转成列表可以确保顺序稳定:
return Response({ 'labels': labels, 'sales': list(total_earn_month.values()), 'total_expenses': list(total_expenses.values()) })
完整修改后的视图代码
class SalesExpensesChart(APIView): """ 视图:列出所有营收与费用 * 需要会话认证(SessionAuthentication) * 仅认证用户可查看自身数据 """ authentication_classes = [SessionAuthentication] permission_classes = [] def get(self, request, format=None): user = Account.objects.get(email=request.user) invoices = user.invoice.all() expenses_to_pay = user.expenses_to_pay.all() expenses_paid = user.expenses_paid.all() # 按月份统计总营收(初始化1-12月为0) total_earn_month = {month: 0 for month in range(1, 13)} for invoice in invoices: invoice_month = invoice.created_at.month total_earn_month[invoice_month] += invoice.total # 按月份统计待支付总费用(修复累加bug+初始化全月份) total_etp_month = {month: 0 for month in range(1, 13)} for expense in expenses_to_pay: etp_month = expense.invoice_date.month total_etp_month[etp_month] += expense.price # 按月份统计已支付总费用(初始化全月份) total_ep_month = {month: 0 for month in range(1, 13)} for expense in expenses_paid: ep_month = expense.purchase_date.month total_ep_month[ep_month] += expense.price # 按月份合并总费用(保持1-12月顺序) total_expenses = {month: total_ep_month[month] + total_etp_month[month] for month in range(1, 13)} # 获取标签 labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] # 打包数据传递至模板(转成列表确保顺序) sales = list(total_earn_month.values()) total_expenses_data = list(total_expenses.values()) return Response({ 'labels': labels, 'sales': sales, 'total_expenses': total_expenses_data })
这样修改后,8月的数据就会准确对应到labels里的August位置,不会再跑到January去,同时也修复了待支付费用的累加问题。
内容的提问来源于stack exchange,提问作者Jannick
相关产品推荐
相关产品推荐

