Flask传JSON渲染Chart.js饼图时报too many values to unpack错误
问题排查结果
当前报错和渲染失败是后端逻辑错误+前端API调用错误共同导致的,分点说明:
后端ValueError: too many values to unpack根因
- 编写的聚合查询每行返回2个值:收支金额总和、对应的收支类型,循环时写
for total_amount, in income_vs_expenses:带了尾逗号,试图把每行结果解包为1个变量,和实际返回的字段数不匹配,直接触发解包错误。 - 就算解包语法修正,写的
total.amount也会报错:查询返回的行是元组,不是对象,不存在amount属性。 - 提前用
json.dumps序列化数据后,前端又调用Jinja2的tojson过滤器,会造成二次转义,JSON格式损坏,前端解析直接失败。
前端饼图渲染失败根因
- Chart初始化的第一个参数要求传入canvas DOM对象或者2D渲染上下文,传了未定义的变量
income_vs_expenses,找不到渲染目标。 - 变量名混用:解析后的数据存在
income_expenses变量里,配置里传参时变量名写错,且硬编码的labels顺序和数据库分组顺序可能不匹配,就算渲染成功也会出现数据和标签错位。 - 页面如果没有提前写对应id的canvas元素,也会导致渲染失败。
修复方案
修正后的后端路由代码
@add_dash.route('/chart') def chart(): # 查询结果格式: [(金额总和, 收支类型), ...] db_result = db.session.query( db.func.sum(IncomeExpenses.amount), IncomeExpenses.type ).group_by(IncomeExpenses.type).order_by(IncomeExpenses.type).all() amount_data = [] type_labels = [] # 正确解包每行两个字段 for total, i_type in db_result: amount_data.append(float(total)) # 转float避免Decimal类型序列化异常 type_labels.append(i_type) # 直接传原生Python列表到模板,不要手动提前json.dumps return render_template( "chart.html", user=current_user, amount_data=amount_data, type_labels=type_labels )
修正后的前端JS代码
先确认模板里提前写了canvas元素:
<canvas id="incomeExpenseChart" width="400" height="400"></canvas>
然后替换JS部分:
{% block javascript %} <script> // tojson过滤器会直接输出合法JS数组,不需要额外JSON.parse const chartAmounts = {{ amount_data|tojson }}; const chartLabels = {{ type_labels|tojson }}; // 获取canvas上下文作为Chart初始化的第一个参数 const ctx = document.getElementById('incomeExpenseChart').getContext('2d'); new Chart(ctx, { type: 'pie', data: { labels: chartLabels, // 用后端返回的动态标签,避免硬编码错位 datasets: [{ label: "Income Vs Expenses", data: chartAmounts, backgroundColor: ['#5DA5DA', '#FAA43A', '#60BD68', '#B276B2', '#E16851', '#FB8267'], borderWidth: 1, hoverBorderColor: "black", hoverBorderWidth: 2, hoverBackgroundColor: 'rgba(154, 245, 140)', pointHoverRadius: 5 }], }, options: { title: { display: true, text: "Income Vs Expenses", fontSize: 20, }, legend: { position: "right", labels: { fontColor: "blue" }, display: true, }, elements: { hitRadius: 3, } } }) </script> {% endblock %}
避坑提示
- 数据库聚合返回的金额如果是Decimal类型,必须转成int/float再传给模板,否则JSON序列化会抛出类型不匹配错误。
- Jinja2的
tojson过滤器本身就会完成JSON序列化和XSS转义,不需要提前在后端做json.dumps,否则会出现双层转义的格式问题。 - 不要硬编码图表标签,直接用查询返回的分组字段生成标签,避免分组顺序变化导致数据和标签对应错误。
内容的提问来源于stack exchange,提问作者burntokeen
相关产品推荐
相关产品推荐

