validators - wtforms flask
Múltiples formularios en una sola página usando matraz y WTForms (4)
Tengo un formulario múltiple en la misma página que envía una solicitud de publicación al mismo controlador en el matraz.
Estoy generando formularios usando wtforms.
¿Cuál es la mejor manera de identificar qué formulario se envía?
Actualmente estoy usando action="?form=oneform"
. Creo que debería haber algún método mejor para lograr lo mismo.
Como las otras respuestas, también asigno un nombre único para cada botón de envío, para cada formulario en la página.
Luego, la acción web del matraz se ve a continuación: tenga en cuenta los parámetros formdata
y obj
, que ayudan a iniciar / preservar los campos del formulario en consecuencia:
@bp.route(''/do-stuff'', methods=[''GET'', ''POST''])
def do_stuff():
result = None
form_1 = None
form_2 = None
form_3 = None
if "submit_1" in request.form:
form_1 = Form1()
result = do_1(form_1)
elif "submit_2" in request.form:
form_2 = Form2()
result = do_2(form_2)
elif "submit_3" in request.form:
form_3 = Form3()
result = do_3(form_3)
if result is not None:
return result
# Pre-populate not submitted forms with default data.
# For the submitted form, leave the fields as they were.
if form_1 is None:
form_1 = Form1(formdata=None, obj=...)
if form_2 is None:
form_2 = Form2(formdata=None, obj=...)
if form_3 is None:
form_3 = Form3(formdata=None, obj=...)
return render_template("page.html", f1=form_1, f2=form_2, f3=form_3)
def do_1(form):
if form.validate_on_submit():
flash("Success 1")
return redirect(url_for(".do-stuff"))
def do_1(form):
if form.validate_on_submit():
flash("Success 2")
return redirect(url_for(".do-stuff"))
def do_3(form):
if form.validate_on_submit():
flash("Success 3")
return redirect(url_for(".do-stuff"))
He estado usando una combinación de dos fragmentos de matraz. El primero agrega un prefijo a un formulario y luego verifica el prefijo con validate_on_submit (). También uso la plantilla de Louis Roché para determinar qué botones se presionan en un formulario .
Para citar a Dan Jacob:
Ejemplo:
form1 = FormA(prefix="form1")
form2 = FormB(prefix="form2")
form3 = FormC(prefix="form3")
Luego, agregue un campo oculto (o simplemente marque un campo de envío):
if form1.validate_on_submit() and form1.submit.data:
Para citar Louis Roché:
Tengo en mi plantilla:
<input type="submit" name="btn" value="Save">
<input type="submit" name="btn" value="Cancel">
Y para averiguar qué botón pasó el lado del servidor que tengo en mi archivo views.py:
if request.form[''btn''] == ''Save'':
something0
else:
something1
La solución anterior tiene un error de validación , cuando un formulario causa un error de validación, ambos formularios muestran un mensaje de error. Cambio el orden de if
para resolver este problema.
Primero, defina su Multiple SubmitField
con diferentes nombres, como este:
class Form1(Form):
name = StringField(''name'')
submit1 = SubmitField(''submit'')
class Form2(Form):
name = StringField(''name'')
submit2 = SubmitField(''submit'')
....
A continuación, agregue un filtro en view.py
:
....
form1 = Form1()
form2 = Form2()
....
if form1.submit1.data and form1.validate(): # notice the order
....
if form2.submit2.data and form2.validate(): # notice the order
....
Ahora el problema fue resuelto.
Si quieres sumergirte en él, entonces continúa leyendo.
Aquí está validate_on_submit()
:
def validate_on_submit(self):
"""
Checks if form has been submitted and if so runs validate. This is
a shortcut, equivalent to ``form.is_submitted() and form.validate()``
"""
return self.is_submitted() and self.validate()
Y aquí está is_submitted()
:
def is_submitted():
"""Consider the form submitted if there is an active request and
the method is ``POST``, ``PUT``, ``PATCH``, or ``DELETE``.
"""
return _is_submitted() # bool(request) and request.method in SUBMIT_METHODS
Cuando llama a form.validate_on_submit()
, comprueba si el formulario envía el formulario por el método HTTP sin importar en qué botón de envío se haya hecho clic. Entonces, el pequeño truco anterior es simplemente agregar un filtro (para verificar si submit tiene datos, es decir, form1.submit1.data
).
Además, cambiamos el orden de if
, de modo que cuando hacemos clic en un envío, solo llama a validate()
en este formulario , evitando el error de validación para ambos formularios.
La historia no ha terminado todavía. Aquí está .data
:
@property
def data(self):
return dict((name, f.data) for name, f in iteritems(self._fields))
Devuelve un dict con nombre de campo (clave) y datos de campo (valor); sin embargo, nuestro botón de envío de dos formularios tiene el mismo nombre (clave).
Cuando hacemos clic en el primer botón de enviar (en form1), la llamada de form1.submit1.data
devuelve un dict como este:
temp = {''submit'': True}
No hay duda cuando llamamos if form1.submit.data:
, devuelve True
.
Cuando hacemos clic en el segundo botón de enviar (en form2), la llamada a .data
en if form1.submit.data:
agrega if form1.submit.data:
una clave-valor en dict, luego la llamada de if form2.submit.data:
agrega otra clave-valor , al final, al dict le gustará esto:
temp = {''submit'': False, ''submit'': True}
Ahora llamamos if form1.submit.data:
, devuelve True
, incluso si el botón de enviar en el que hicimos clic estaba en form2.
Es por eso que tenemos que definir este dos SubmitField
con diferentes nombres. Por cierto, ¡gracias por leer (hasta aquí)!
Actualizar
Hay otra forma de manejar formularios múltiples en una página. Puede usar vistas múltiples para manejar formularios. Por ejemplo:
...
@app.route(''/'')
def index():
register_form = RegisterForm()
login_form = LoginForm()
return render_template(''index.html'', register_form=register_form, login_form=login_form)
@app.route(''/register'', methods=[''POST''])
def register():
register_form = RegisterForm()
login_form = LoginForm()
if register_form.validate_on_submit():
... # handle the register form
# render the same template to pass the error message
# or pass `form.errors` with `flash()` or `session` then redirect to /
return render_template(''index.html'', register_form=register_form, login_form=login_form)
@app.route(''/login'', methods=[''POST''])
def login():
register_form = RegisterForm()
login_form = LoginForm()
if login_form.validate_on_submit():
... # handle the login form
# render the same template to pass the error message
# or pass `form.errors` with `flash()` or `session` then redirect to /
return render_template(''index.html'', register_form=register_form, login_form=login_form)
En la plantilla (index.html), debe representar ambos formularios y establecer el atributo de action
en la vista de destino:
<h1>Register</h1>
<form action="{{ url_for(''register'') }}" method="post">
{{ register_form.username }}
{{ register_form.password }}
{{ register_form.email }}
</form>
<h1>Login</h1>
<form action="{{ url_for(''login'') }}" method="post">
{{ login_form.username }}
{{ login_form.password }}
</form>
Una forma simple es tener diferentes nombres para diferentes campos de envío. Para un ejemplo:
forms.py:
class Login(Form):
...
login = SubmitField(''Login'')
class Register(Form):
...
register = SubmitField(''Register'')
views.py:
@main.route(''/'')
def index():
login_form = Login()
register_form = Register()
if login_form.validate_on_submit() and login_form.login.data:
print "Login form is submitted"
elif register_form.validate_on_submit() and register_form.register.data:
print "Register form is submitted"
...