【问题标题】:WTF.forms "Not a Valid Choice" error for SelectField dynamically updated动态更新 SelectField 的 WTF.forms “Not a Valid Choice”错误
【发布时间】:2022-01-06 15:11:17
【问题描述】:

我看到了一些关于这个的类似问题,但没有一个有明确的答案。所以我会尽量说清楚。

我最初发布了这个问题,我的真实代码很平静,因为它依赖于与 Oracle 数据库连接的连接,因此很难有人复制。所以我做了一个简化版。

所以。第一件事。我正在做的是使用 Flask 创建一个带有 4 SelectField 的表单。第一个 SelectField 的选项在类定义中定义(在视图部分中)。其他 3 个 SelecteField 的选择是根据前面字段的选择动态定义的。

问题是,当我提交表单时,我收到这 3 个字段的“无效选择”。

我按照本教程创建了这个。

https://www.youtube.com/watch?v=I2dJuNwlIH0

视图(form_toy.py)代码如下

from flask import Flask,session,request,render_template,jsonify,url_for,redirect
from flask_wtf import FlaskForm
from wtforms import SelectField,SubmitField
from wtforms.validators import DataRequired
from flask_bootstrap import Bootstrap
import pandas as pd

app = Flask(__name__)
app.config['SECRET_KEY']='asdas8789)(&673'
bootstrap=Bootstrap(app)

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)
    plant = SelectField("Plant",coerce=int,choices=[])
    ctrl = SelectField("Ctrl",coerce=int,choices=[])
    subctrl = SelectField("Sub",coerce=int,choices=[])
    submit = SubmitField("OK")

@app.route("/",methods=['GET','POST'])
def index():
    site = ''
    plant = ''
    ctrl = ''
    subctrl = ''
    form =TestForm()
    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)

@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()
    
    i=1
    for _,p in df2.iteritems():
        plants_array.append({'id':i,'plant':p})
        i=i+1

    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()
    
    i=1
    for _,c in df3.iteritems():
        ctrls_array.append({'id':i,'ctrl':c})
        i=i+1

    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()
    
    i=1
    for _,s in df4.iteritems():
        subctrls_array.append({'id':i,'subctrl':s})
        i=i+1

    return jsonify({'subctrls':subctrls_array})

模板代码(index.html)在这里(为了简单起见,我把所有东西都放在同一个文件中)

{% import "bootstrap/wtf.html" as wtf %}

{% block title %}FORM_VALIDATION{% endblock %}

{{ wtf.quick_form(form)}}
{% if form.errors %}
    {{ form.errors}}
{% endif %}

<script>
    var site_select = document.getElementById("site");
    var plant_select = document.getElementById("plant");
    var ctrl_select = document.getElementById("ctrl");
    var subctrl_select = document.getElementById("subctrl")
    var plant1=''
    var ctrl1 = ''

    site_select.onchange = function(){
        var site = site_select.options[site_select.selectedIndex].text;
        get_options_plants(site);
    };

    plant_select.onchange = function(){
        // get controllers
        var site = site_select.options[site_select.selectedIndex].text;
        var plant = plant_select.options[plant_select.selectedIndex].text;
        get_options_ctrl(site,plant)
    };

    ctrl_select.onchange = function(){
        //get subctrollers
        var site = site_select.options[site_select.selectedIndex].text;
        var plant = plant_select.options[plant_select.selectedIndex].text;
        var ctrl = ctrl_select.options[ctrl_select.selectedIndex].text;
        get_options_subctrl(site,plant,ctrl)
    };

    function get_options_plants(site){
        //get plant;
        var site = site_select.options[site_select.selectedIndex].text;
        fetch('plant/'+site).then(function(response){
            response.json().then(function(data){
                var optionHTML='';
               
                for (var plant of data.plants){
                    optionHTML += '<option value=' + parseInt(plant.id) + '>' + plant.plant + '</option>'
                    if (plant1==''){
                        plant1=plant.plant
                    }
                }
                plant_select.innerHTML = optionHTML;
                get_options_ctrl(plant1)                 
            })       
        
        });
    };

    function get_options_ctrl(site,plant){
        //get controllers
        var site = site_select.options[site_select.selectedIndex].text;
        var plant = plant_select.options[plant_select.selectedIndex].text;
        fetch('ctrl/'+site+'/'+plant).then(function(response){
            response.json().then(function(data){
                var optionHTML='';
                for (var ctrl of data.ctrls){
                    optionHTML += '<option value=' + parseInt(ctrl.id) + '>' + ctrl.ctrl + '</option>'
                    if (ctrl1==''){
                        ctrl1=ctrl.ctrl
                    }
                }
                ctrl_select.innerHTML = optionHTML;
                get_options_subctrl(site,ctrl1)
            })
        });
        plant1='';
    }

    function get_options_subctrl(site,plant,ctrl){
        //get subcontrollers
        var site = site_select.options[site_select.selectedIndex].text;
        var plant = plant_select.options[plant_select.selectedIndex].text;
        var ctrl = ctrl_select.options[ctrl_select.selectedIndex].text;

        fetch('subctrl/'+site+'/'+plant+'/'+ctrl).then(function(response){
            response.json().then(function(data){
                var optionHTML='';
                for (var sub of data.subctrls){
                    optionHTML += '<option value=' + parseInt(sub.id) + '>' + sub.subctrl + '</option>'
                }
                subctrl_select.innerHTML = optionHTML;
            })
        });
        ctrl1='';
    }

    //call get_option_plants() to initiate menus
    get_options_plants();
</script>

最后,用于填充字段的数据存储在使用 pandas 读取的 csv 文件 (db.csv) 中。

site,plant,ctrl,subctrl
s1,p11,c111,s1a
s1,p11,c111,s2b
s1,p11,c111,s3c
s1,p11,c211,s1d
s1,p11,c211,s2e
s1,p21,c121,s1f
s1,p21,c121,s2g
s1,p21,c221,s1h
s1,p21,c221,s2i
s1,p21,c221,s3j
s2,p12,c112,s1k
s2,p12,c112,s2l
s2,p12,c112,s3m
s2,p12,c222,s1n
s2,p12,c222,s2o
s2,p22,c122,s1p
s2,p22,c122,s2q
s2,p22,c222,s1r
s2,p22,c222,s2s
s2,p22,c222,s3t

我会感谢任何帮助,因为我是这个网络应用领域的新手。

我发现了一些与这个问题相关的帖子。

Not a Valid Choice for Dynamic Select Field WTFORMS

但我按照说明进行操作,但并没有解决我的问题。

谢谢

【问题讨论】:

    标签: javascript python validation flask flask-wtforms


    【解决方案1】:

    好的。我弄清楚了问题所在。在视图(后端)中,我需要使用每个字段中可以拥有的所有可能选项来初始化参数选择。我通过阅读此链接意识到这一点

    https://coderedirect.com/questions/192536/populate-wtforms-select-field-using-value-selected-from-previous-field

    这样做。我可以使用 JS 代码根据前面字段中选择的值向用户显示所需的值。

    所以。对于任何可能感兴趣的人,这是代码的更新版本。这不是实现它的最有效方式(我猜),但它可以展示它是如何工作的。

    from flask import Flask,session,request,render_template,jsonify,url_for,redirect,g
    from flask_wtf import FlaskForm
    from wtforms import SelectField,SubmitField
    from wtforms.validators import DataRequired
    from flask_bootstrap import Bootstrap
    import pandas as pd
    
    app = Flask(__name__)
    app.config['SECRET_KEY']='asdas8789)(&673'
    bootstrap=Bootstrap(app)
    
    class FormChoices():
        
        def __init__(self):
            self.choices_site={}
            self.choices_plant={}
            self.choices_ctrl={}
            self.choices_subctrl={}
            n=0 #site
            i=0 #plant
            j=0 #ctrl
            k=0 #subctrl
            df=pd.read_csv('db.csv')
            df1=df['site'].drop_duplicates() 
            for _,s in df1.iteritems():
                self.choices_site[n]=s
                df2=df.loc[df['site']==s]
                df3=df2['plant'].drop_duplicates()
                
                self.choices_plant[n]={}
                self.choices_ctrl[n]={}
                self.choices_subctrl[n]={}
                
                for _,p in df3.iteritems():
                    self.choices_plant[n][i]=p
                    
                    df4=df.loc[df['site']==s]
                    df5=df4.loc[df4['plant']==p]
                    df6=df5['ctrl'].drop_duplicates()
                    
                    self.choices_ctrl[n][i]={}
                    self.choices_subctrl[n][i]={}
                    
                    for _,c in df6.iteritems():
                        self.choices_ctrl[n][i][j]=c
                        
                        df7=df.loc[df['site']==s]
                        df8=df7.loc[df7['plant']==p]
                        df9=df8.loc[df8['ctrl']==c]
                        df10=df9['subctrl'].drop_duplicates()
                        
                        self.choices_subctrl[n][i][j]={}
                        
                        for _,sub in df10.iteritems():
                            self.choices_subctrl[n][i][j][k]=sub
                            k=k+1
                        j=j+1
                    i=i+1
                n=n+1
            
    class TestForm(FlaskForm):
        
        df=pd.read_csv('db.csv')
        df1=df['site'].drop_duplicates()
        choices_site=[]
        choices_plant=[]
        choices_ctrl=[]
        choices_subctrl=[]
    
        formc = FormChoices()
        for k0,v0 in formc.choices_site.items():
            choices_site.append((k0,v0))
            for k1,v1 in formc.choices_plant[k0].items():
                choices_plant.append((k1,v1))
                for k2,v2 in formc.choices_ctrl[k0][k1].items():
                    choices_ctrl.append((k2,v2))
                    for k3,v3 in formc.choices_subctrl[k0][k1][k2].items():
                        choices_subctrl.append((k3,v3))
        
        site = SelectField("Site",coerce=int,choices=choices_site)
        plant = SelectField("Plant",coerce=int,choices=choices_plant)
        ctrl = SelectField("Ctrl",coerce=int,choices=choices_ctrl)
        subctrl = SelectField("Sub",coerce=int,choices=choices_subctrl)
        submit = SubmitField("OK")
    
    @app.route("/",methods=['GET','POST'])
    def index():
        site = ''
        plant = ''
        ctrl = ''
        subctrl = ''
        x = None
        g.form = TestForm()
        if g.form.validate_on_submit(): #request.method=='POST':
            site = g.form.site.data
            plant = g.form.plant.data
            ctrl = g.form.ctrl.data
            subctrl = g.form.subctrl.data
            ch=FormChoices()
            x=ch.choices_ctrl[plant][ctrl]
    
            return render_template('test.html',
                                   x=plant,
                                   y=ctrl,
                                   z=subctrl)
        else:
            return render_template('index.html',
                                form=g.form,
                                site=site,
                                plant=plant,
                                ctrl=ctrl,
                                subctrl=subctrl,
                                x=plant,
                                y=ctrl,
                                z=subctrl)
    
    @app.route("/plant/<site_id>")
    def GetPlant(site_id):
        plants_array = []
    
        ch=FormChoices()
        
        for k,v in ch.choices_plant[int(site_id)].items():
            plants_array.append({'id':k,'plant':v})
            
        return jsonify({'plants':plants_array})
    
    @app.route("/ctrl/<site_id>/<plant_id>")
    def GetCtrl(site_id,plant_id):
        ctrls_array = []
        
        ch=FormChoices()
        for k,v in ch.choices_ctrl[int(site_id)][int(plant_id)].items():
            ctrls_array.append({'id':k,'ctrl':v})
            
        return jsonify({'ctrls':ctrls_array})
    
    @app.route("/subctrl/<site_id>/<plant_id>/<ctrl_id>")
    def GetSubCtrl(site_id,plant_id,ctrl_id):
        subctrls_array = []
        
        ch=FormChoices()
        for k,v in ch.choices_subctrl[int(site_id)][int(plant_id)][int(ctrl_id)].items():
            subctrls_array.append({'id':k,'subctrl':v})
    
        return jsonify({'subctrls':subctrls_array})
    

    这里是html

    {% import "bootstrap/wtf.html" as wtf %}
    
    {% block title %}FORM_VALIDATION{% endblock %}
    
    {{ wtf.quick_form(form)}}
    {% if form.errors %}
        {{ form.errors}}
    {% endif %}
    <h2>{{x}},{{y}},{{z}}</h2>
    
    <script>
        var site_select = document.getElementById("site");
        var plant_select = document.getElementById("plant");
        var ctrl_select = document.getElementById("ctrl");
        var subctrl_select = document.getElementById("subctrl")
        var plant1 = ''
        var ctrl1 = ''
    
        site_select.onchange = function(){
            var site_id = site_select.value;
            get_options_plants(site_id);
        };
    
        plant_select.onchange = function(){
            // get controllers
            var site_id = site_select.value
            var plant_id = plant_select.value;
            get_options_ctrl(site_id,plant_id)
        };
    
        ctrl_select.onchange = function(){
            //get subctrollers
            var site_id = site_select.value
            var plant_id = plant_select.value;
            var ctrl_id = ctrl_select.value;
            get_options_subctrl(site_id,plant_id,ctrl_id)
        };
    
        function get_options_plants(site){
            //get plant;
            var site_id = site_select.value;
            fetch('plant/'+site_id).then(function(response){
                response.json().then(function(data){
                    var optionHTML='';
                   
                    for (var plant of data.plants){
                        optionHTML += '<option value=' + parseInt(plant.id) + '>' + plant.plant + '</option>'
                        if (plant1==''){
                            plant1=plant.plant
                        }
                    }
                    plant_select.innerHTML = optionHTML;
                    get_options_ctrl(plant1)                 
                })       
            
            });
        };
    
        function get_options_ctrl(site,plant){
            //get controllers
            var site_id = site_select.value
            var plant_id = plant_select.value;
            fetch('ctrl/'+site_id+'/'+plant_id).then(function(response){
                response.json().then(function(data){
                    var optionHTML='';
                    for (var ctrl of data.ctrls){
                        optionHTML += '<option value=' + parseInt(ctrl.id) + '>' + ctrl.ctrl + '</option>'
                        if (ctrl1==''){
                            ctrl1=ctrl.ctrl
                        }
                    }
                    ctrl_select.innerHTML = optionHTML;
                    get_options_subctrl(site,ctrl1)
                })
            });
            plant1='';
        }
    
        function get_options_subctrl(site,plant,ctrl){
            //get subcontrollers
            var site_id = site_select.value
            var plant_id = plant_select.value;
            var ctrl_id = ctrl_select.value;
    
            fetch('subctrl/'+site_id+'/'+plant_id+'/'+ctrl_id).then(function(response){
                response.json().then(function(data){
                    var optionHTML='';
                    for (var sub of data.subctrls){
                        optionHTML += '<option value=' + parseInt(sub.id) + '>' + sub.subctrl + '</option>'
                    }
                    subctrl_select.innerHTML = optionHTML;
                })
            });
            ctrl1='';
        }
    
        //call get_option_plants() to initiate menus
        get_options_plants();
    </script>
    

    这里是我用于测试的另一个 html。

    {% import "bootstrap/wtf.html" as wtf %}
    
    {% block title %}FORM_VALIDATION{% endblock %}
    
    <h2>{{x}},{{y}},{{z}}</h2>
    

    希望这对某人有用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-01
      • 1970-01-01
      • 2012-09-11
      • 2019-08-03
      • 1970-01-01
      • 2023-01-17
      相关资源
      最近更新 更多