Flask WTF.forms动态SelectField提交报Not a Valid Choice错误求助
问题原因
- WTForms的
SelectField默认会校验提交值是否属于表单类定义时预设的choices列表,你通过前端JS动态新增的选项不会同步到后端的表单实例,因此提交时会触发Not a valid choice校验错误 - 你当前的动态接口返回的选项id是请求时临时生成的自增序号,和数据本身无绑定,不同请求下同一个值的id可能不一致,也会增加校验逻辑的复杂度
解决方案
1. 后端逻辑修改(form_toy.py)
第一步:调整表单字段配置
将动态字段的coerce改为字符串类型,后续直接用实际字段值作为选项value,避免临时id的匹配问题
class TestForm(FlaskForm): df=pd.read_csv('db.csv') df1=df['site'].drop_duplicates() choices_site=[] i=1 for _,v in df1.iteritems(): choices_site.append((i,v)) i=i+1 site = SelectField("Site",coerce=int,choices=choices_site) # 调整动态字段的coerce为字符串类型 plant = SelectField("Plant",coerce=str,choices=[]) ctrl = SelectField("Ctrl",coerce=str,choices=[]) subctrl = SelectField("Sub",coerce=str,choices=[]) submit = SubmitField("OK")
第二步:校验前动态回填choices
在index路由接收POST请求后,先根据已提交的上级字段值,动态补全当前字段的可选列表,再执行校验
@app.route("/",methods=['GET','POST']) def index(): site = '' plant = '' ctrl = '' subctrl = '' form =TestForm() df = pd.read_csv('db.csv') # POST请求时先补全所有动态字段的可选值再校验 if request.method == 'POST': # 获取选中的site文本值 selected_site = dict(form.site.choices).get(int(form.site.data)) # 补全plant的choices plant_list = df[df['site']==selected_site]['plant'].drop_duplicates() form.plant.choices = [(v, v) for v in plant_list] # 补全ctrl的choices selected_plant = form.plant.data if selected_plant: ctrl_list = df[(df['site']==selected_site)&(df['plant']==selected_plant)]['ctrl'].drop_duplicates() form.ctrl.choices = [(v, v) for v in ctrl_list] # 补全subctrl的choices selected_ctrl = form.ctrl.data if selected_ctrl: subctrl_list = df[(df['site']==selected_site)&(df['plant']==selected_plant)&(df['ctrl']==selected_ctrl)]['subctrl'].drop_duplicates() form.subctrl.choices = [(v, v) for v in subctrl_list] if form.validate_on_submit(): site = form.site.data plant = form.plant.data ctrl = form.ctrl.data subctrl = form.subctrl.data return render_template('index.html', form=form, site=site, plant=plant, ctrl=ctrl, subctrl=subctrl)
第三步:修改动态接口返回值
将接口返回的id改为实际的字段值,和后端校验逻辑对齐
@app.route("/plant/<site>") def GetPlant(site): plants_array = [] df=pd.read_csv('db.csv') df1=df.loc[df['site']==site] df2=df1['plant'].drop_duplicates() for _,p in df2.iteritems(): plants_array.append({'id':p,'plant':p}) return jsonify({'plants':plants_array}) @app.route("/ctrl/<site>/<plant>") def GetCtrl(site,plant): ctrls_array = [] df=pd.read_csv('db.csv') df1=df.loc[df['site']==site] df2=df1.loc[df1['plant']==plant] df3=df2['ctrl'].drop_duplicates() for _,c in df3.iteritems(): ctrls_array.append({'id':c,'ctrl':c}) return jsonify({'ctrls':ctrls_array}) @app.route("/subctrl/<site>/<plant>/<ctrl>") def GetSubCtrl(site,plant,ctrl): subctrls_array = [] df=pd.read_csv('db.csv') df1=df.loc[df['site']==site] df2=df1.loc[df1['plant']==plant] df3=df2.loc[df2['ctrl']==ctrl] df4=df3['subctrl'].drop_duplicates() for _,s in df4.iteritems(): subctrls_array.append({'id':s,'subctrl':s}) return jsonify({'subctrls':subctrls_array})
2. 前端逻辑修改(index.html)
修复初始化参数缺失问题,调整option生成逻辑,删除不必要的id转换
// 末尾的初始化调用修改为传入默认site值 var init_site = site_select.options[site_select.selectedIndex].text; get_options_plants(init_site);
同时删除JS中所有parseInt(plant.id)、parseInt(ctrl.id)、parseInt(sub.id)的parseInt转换,直接用返回的id值即可。
内容的提问来源于stack exchange,提问作者Dariva
相关产品推荐
相关产品推荐

