es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Error “No es una elección válida” en WTF.forms para SelectField actualizado dinámicamente.

Vi algunas preguntas similares sobre esta, pero ninguna de ellas tenía una respuesta clara. Así que intentaré hacerlo lo más claro posible.

Inicialmente publiqué la pregunta con parte de mi código real, que no era fácil de replicar ya que dependía de la conexión a la base de datos Oracle. Por lo tanto, hice una versión simplificada.

Así que, lo primero es lo primero. Lo que estoy haciendo es crear un formulario con 4 SelectField utilizando Flask. Las opciones para el primer SelectField están definidas en la definición de la clase (en la parte de la vista). Las opciones de los otros 3 SelecteFields se definen dinámicamente en función de las opciones de los campos anteriores.

El problema es que cuando envío el formulario, recibo un “No es una opción válida” para estos 3 campos.

Seguí este tutorial para crear esto.

El código de la vista (form_toy.py) es el siguiente

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/“)
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//“)
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///“)
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})

El código de la plantilla (index.html) está aquí (puse todo en el mismo archivo por motivos de simplificación)

{% import “bootstrap/wtf.html” as wtf %}

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

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

Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    El problema que estás enfrentando es con la validación de las opciones dinámicas de SelectField en tus WTForms. Cuando se envía el formulario, Flask-WTF verifica si los datos enviados para cada campo son una opción válida según lo definido en el parámetro choices de SelectField.

    En tu caso, las opciones para los tres SelectFields dinámicos (plant, ctrl, subctrl) se cargan de forma asíncrona usando JavaScript después de que la página se ha cargado. Flask-WTF no está al tanto de estas opciones dinámicas y por lo tanto genera el error “No es una opción válida”.

    Para resolver esto, es necesario anular la validación por defecto para estos campos en la clase del formulario. Puede hacer esto creando una función validadora personalizada que compruebe si los datos enviados son una de las opciones válidas que se generaron de forma asíncrona. Aquí tienes un ejemplo:

    from wtforms.validators import ValidationError
    
    class ValidChoice:
        def __init__(self, choices):
            self.choices = choices
    
        def __call__(self, form, field):
            if field.data not in [choice[0] for choice in self.choices]:
                raise ValidationError('No es una opción válida')
    

    Luego, en la clase TestForm, puedes usar esta validación personalizada para los SelectFields dinámicos de la siguiente manera:

    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("Sitio",coerce=int,choices=choices_site)
        plant = SelectField("Planta",coerce=int,choices=[])
        ctrl = SelectField("Ctrl",coerce=int,choices=[])
        subctrl = SelectField("Sub",coerce=int,choices=[])
        submit = SubmitField("OK")
    
        def validate_plant(self, field):
            if not field.data:
                raise ValidationError('Por favor selecciona una planta')
            else:
                site_id = self.site.data
                plant_id = field.data
                valid_plants = get_valid_choices(site_id)
                if plant_id not in [p[0] for p in valid_plants]:
                    raise ValidationError('No es una opción válida')
    
        def validate_ctrl(self, field):
            if not field.data:
                raise ValidationError('Por favor selecciona un controlador')
            else:
                site_id = self.site.data
                plant_id = self.plant.data
                ctrl_id = field.data
                valid_ctrls = get_valid_choices(site_id, plant_id)
                if ctrl_id not in [c[0] for c in valid_ctrls]:
                    raise ValidationError('No es una opción válida')
    
        def validate_subctrl(self, field):
            if not field.data:
                raise ValidationError('Por favor selecciona un subcontrolador')
            else:
                site_id = self.site.data
                plant_id = self.plant.data
                ctrl_id = self.ctrl.data
                subctrl_id = field.data
                valid_subctrls = get_valid_choices(site_id, plant_id, ctrl_id)
                if subctrl_id not in [s[0] for s in valid_subctrls]:
                    raise ValidationError('No es una opción válida')
    

    Aquí, hemos agregado una función validadora personalizada para cada uno de los SelectFields dinámicos (plant, ctrl, subctrl) que comprueba si los datos enviados son una de las opciones válidas que se generaron de forma asíncrona. La función get_valid_choices() debe ser una función que recupere las opciones válidas para cada uno de los SelectFields dinámicos en función de las opciones de los campos anteriores.

    Al agregar estas validaciones personalizadas, Flask-WTF las usará en lugar de la validación por defecto y ya no debería generarse el error “No es una opción válida”.

    ¡Espero que esto ayude! Hazme saber si tienes más preguntas.

Comments are closed.