Flask路由中HTML下拉列表传参引发UnboundLocalError问题
解决 Flask 中 UnboundLocalError 及表单提交、绘图的问题
看起来你遇到的UnboundLocalError主要是因为变量在某些分支下没有被赋值就尝试返回/使用,再加上表单设计和绘图流程的小问题,咱们一步步来修复:
1. 核心错误:未赋值的变量
问题分析
- 在
testt1()、testt2()和regrplot()函数里,你只有在POST请求时才会给x1/y1赋值,但如果是GET请求(比如用户直接访问这些路由),或者选中的字段不在任何一个xxx_fields列表里,这些变量就根本没被定义,直接返回/使用自然会报错。 - 另外
regrplot()里你把Reg_df的定义注释掉了,后面直接用Reg_df也会报未定义错误。
修复方案
- 给变量设置默认值,或者确保所有分支都覆盖到赋值逻辑,同时处理异常情况;
- 取消
Reg_df的注释,确保合并后的DataFrame正确生成。
2. 表单设计问题:两个独立表单导致参数不全
问题分析
你现在把两个下拉框放在两个独立的<form>里,点击提交按钮时,只会提交第二个表单的df_variable2参数,第一个下拉框的df_variable1根本不会被传到后端,导致regrplot()里拿不到x轴的变量。
修复方案
把两个下拉框合并到同一个表单里,提交按钮放在这个表单内部,这样点击提交时两个参数都会被发送。
3. 绘图与静态资源的问题
问题分析
- Flask默认从
static文件夹读取静态资源,你直接把图片保存到根目录,前端html无法正确加载; - Seaborn依赖Matplotlib,在Flask这类非交互式环境下,需要设置Matplotlib使用非交互式后端,否则可能会有渲染错误;
- 绘图后需要关闭Matplotlib的画布,避免内存泄漏。
完整修复后的代码
修改后的 dropdown.html
<body> <form name="reg_form" action="/regrplot" method="POST"> <fieldset> <legend>Table Variable 1 (X轴)</legend> <p> <label>Select</label> <select name="df_variable1"> {% for each in dropdown_fields %} <option value="{{each}}">{{each}}</option> {% endfor %} </select> </p> </fieldset> <fieldset> <legend>Table Variable 2 (Y轴)</legend> <p> <label>Select</label> <select name="df_variable2"> {% for each in dropdown_fields %} <option value="{{each}}">{{each}}</option> {% endfor %} </select> </p> </fieldset> <input type="submit" class="btn btn-default" value="生成回归图"> </form> </body>
修改后的 app.py
import pandas as pd import seaborn as sns import matplotlib.pyplot as plt from flask import Flask, render_template, request # 先设置Matplotlib非交互式后端,避免Flask环境下的渲染问题 plt.switch_backend('Agg') app = Flask(__name__) # 注意:这里假设你已经正确加载了book_data、censusLoad的DataFrame # 修正获取列名的方式:应该取DataFrame的columns,而不是DataFrame本身 book_fields = list(book_data.df_yearbook.columns) census1_fields = list(censusLoad.df_full1.columns) census2_fields = list(censusLoad.df_full2.columns) dropdown_fields = book_fields + census1_fields + census2_fields @app.route("/dropdown") def dropdownList(): return render_template('dropdown.html', dropdown_fields=dropdown_fields) @app.route("/regrplot", methods=['GET','POST']) def regrplot(): # 初始化默认值,避免未赋值错误 x_df = None y_df = None df_variable1 = None df_variable2 = None if request.method == 'POST': df_variable1 = request.form.get('df_variable1') df_variable2 = request.form.get('df_variable2') # 匹配X轴对应的DataFrame if df_variable1 in book_fields: x_df = book_data.df_yearbook elif df_variable1 in census1_fields: x_df = censusLoad.df_full1 elif df_variable1 in census2_fields: x_df = censusLoad.df_full2 # 匹配Y轴对应的DataFrame if df_variable2 in book_fields: y_df = book_data.df_yearbook elif df_variable2 in census1_fields: y_df = censusLoad.df_full1 elif df_variable2 in census2_fields: y_df = censusLoad.df_full2 # 检查DataFrame是否获取成功,以及是否有year列 if x_df is not None and y_df is not None and 'year' in x_df.columns and 'year' in y_df.columns: # 按year合并DataFrame Reg_df = x_df.merge(y_df, how="inner", on='year') # 检查选中的列是否存在于合并后的DataFrame if df_variable1 in Reg_df.columns and df_variable2 in Reg_df.columns: # 生成回归图 plt.figure(figsize=(10,6)) sns.regplot(x=df_variable1, y=df_variable2, data=Reg_df) # 保存到static文件夹,方便前端访问 plt.savefig('static/regrplot.png') plt.close() # 关闭画布,释放内存 # 不管POST还是GET,都返回绘图页面(如果有图就显示,没有就显示空白) return render_template("regression.html") if __name__ == "__main__": app.run(debug=True)
修改后的 regression.html
<!DOCTYPE HTML> <html lang="en"> <head> <title>回归图结果</title> <meta charset="UTF-8" /> </head> <body> <!-- 使用Flask的url_for生成静态资源路径,确保正确加载 --> <img src="{{ url_for('static', filename='regrplot.png') }}" alt="回归图"> </body> </html>
额外注意事项
- 确保
book_data和censusLoad的DataFrame已经正确加载,并且包含year列; - 如果你的DataFrame列名有重复(比如不同DataFrame有相同列名),可以考虑在合并时给列名加前缀,避免混淆;
- 可以在后端增加错误提示,比如当用户选择的字段不存在或者合并失败时,在前端显示提示信息,提升用户体验。
内容的提问来源于stack exchange,提问作者hSin
相关产品推荐
相关产品推荐

