flask form.validate_on_submit() fails using wtforms
You call form.validate()
everytime someone visits '/'
. When the request is a GET, there is no form data causing validation to fail. You only want to try to validate the form when the request is a POST.
One way to do that is to check the request's method.
if request.method == 'POST':
if form.validate():
session['dataserver'] = ds = form.dataserver.data
return redirect(url_for('login'))
else:
flash('Failed validation')
Another common way to do this is with validate_on_submit
. It handles checking for POSTs as well as validating the form.
if form.validate_on_submit():
session['dataserver'] = ds = form.dataserver.data
return redirect(url_for('login'))
Here you'd lose your ability to flash your 'validation failed'
message. That may be acceptable, though, because you can check for form errors in your template.
{% if form.errors %}
failed validation
{% endif %}
UPDATE
If you want to see the errors (which you may not), you can print them in the template instead of the generic "failed validation" message.
{% if form.errors %}
{{ form.errors }}
{% endif %}
bhudson
Updated on June 04, 2022Comments
-
bhudson almost 2 years
Using @dirn's suggestions, the validation error does not display anymore, but it still seems to fail as neither the print statement in the root() function displays/runs nor does the new form.errors show.
App Code:
#!/usr/bin/env python import cherrypy import os from flask import Flask, render_template, redirect, url_for, session, request, flash, abort, Markup from flask.ext.bootstrap import Bootstrap from sybload import funcs, forms app = Flask(__name__) app.debug = True app.config['SECRET_KEY'] = os.urandom(24) app.config['CSRF_ENABLED'] = True bootstrap = Bootstrap(app) dataserver_info = funcs.get_dataserver_info() dataservers = funcs.get_dataservers(dataserver_info) @app.route('/', methods=['GET', 'POST']) def root(): session.clear() form = forms.DataServersForm() form.dataserver.choices = zip(dataservers, dataservers) if form.validate_on_submit(): session['dataserver'] = form.dataserver.data # print statement below never runs print session['dataserver'], form.dataserver.data return redirect(url_for('login')) return render_template('root.html', title='Sybase Load App', form=form) def run_server(): cherrypy.tree.graft(app, '/') cherrypy.config.update({ 'log.access_file': 'logs/access.log', 'log.error_file': 'logs/errors.log', 'engine.autoreload_on': True, 'log.screen': True, 'server.socket_host': '0.0.0.0', 'server.socket_port': 5000, 'tools.sessions.on': True, 'tools.sessions.secure': True, 'tools.sessions.httponly': True }) cherrypy.engine.start() cherrypy.engine.block() if __name__ == '__main__': run_server()
Template (jinja2):
{% block body %} <form method='post' action='{{ url_for('login') }}'> {{ form.hidden_tag() }} {{ form.dataserver.label }}<br> {{ form.dataserver }}<br><br> {{ form.submit }} </form> <!-- Below never displays --> {% if form.errors %} failed validation {% endif %} {% endblock %}
Form:
from flask.ext.wtf import Form from wtforms import StringField, PasswordField, SelectField, SelectMultipleField, SubmitField, BooleanField from wtforms.validators import Required import funcs class DataServersForm(Form): dataserver = SelectField('Dataservers', validators=[Required()]) submit = SubmitField('Submit')
-
bhudson about 9 yearsAfter applying both of your suggestions, I believe there is still something wrong. The session['dataserver'] is never set properly.
@app.route('/', methods=['GET', 'POST']) def root(): session.clear() form = forms.DataServersForm() form.dataserver.choices = zip(dataservers, dataservers) if form.validate_on_submit(): session['dataserver'] = form.dataserver.data print session['dataserver'], form.dataserver.data return redirect(url_for('login')) return render_template('root.html', title='Sybase Load App', form=form)
-
dirn about 9 yearsCode doesn't work so well in comments, please update your question instead.
-
dirn about 9 yearsHave you tried inspecting
form.errors
to see if the form isn't validating? -
bhudson about 9 yearsYes. form.errors before form.validate_on_submit() yields an empty dict, while it never even runs (e.g. print form.errors) if it's inside the "if form.validate_on_submit():" block.
-
dirn about 9 yearsIf form validation fails, nothing inside the
if
will happen. You need to check it after (outside) theif
. One such place to do so would be in the template like I mentioned in the answer. -
bhudson about 9 yearsThanks for your help! I managed to fix the last issue. It was validating properly, but the statement "session['dataserver'] = form.dataserver.data" had to be changed to "session['dataserver'] = request.form['dataserver']". I'm not quite sure why (get vs. post maybe?). It makes me think I need "if request.method == 'POST' and form.validate_on_submit():" instead.
-
bhudson about 9 yearsNote: I also removed the "action='{{ url_for('login') }}'" line in my template.
-
Admin over 2 yearsYour answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.