Flask - wtforms: Validation always false
Asked Answered
T

4

17

First, I'm new to python and Flask, so I'm sorry if my question is dumb. I search it but never found an answer (which should be an "easy" one I guess).

I wanted to add a contact page in my website, I found this tutorial so I followed it. Everything worked fine until the forms validation. I only use Required and form.validate() always returned false. If I don't touch my code, and I remove every Required in the form class, it works fine, form.validate() returns true.

I don't really understand why, I read a lot that validate_on_submit() should be used, but I'm getting an error if I use it: *'ClassName' object has no attribute 'validate_on_submit'*

Here's the relevant parts of the code:

Index.py

@app.route('/contact', methods=['GET','POST'])
def contact():
form = ContactForm()

if request.method == 'POST':
    if form.validate() == False:
        flash('All Fields are required.')
        return render_template('contact.html', form=form)
    else:
        return 'Form posted'
elif request.method == 'GET':
    return render_template('contact.html', form=form)

forms.py

from wtforms import Form, TextField, TextAreaField, SubmitField, validators,ValidationError 

class ContactForm(Form):
  name = TextField("Name", [validators.Required()])
  email = TextField("Email")
  subject = TextField("Subject")
  message = TextAreaField("Message")
  submit = SubmitField("Send")

contact.html

<div id="contact">
    {% for message in get_flashed_messages() %}
        <div class="flash">{{ message }}</div>
    {% endfor %}
  <form action="{{ url_for('contact') }}" method=post>

    {{ form.name.label }}
    {{ form.name }}

    {{ form.email.label }}
    {{ form.email }}

    {{ form.subject.label }}
    {{ form.subject }}

    {{ form.message.label }}
    {{ form.message }}

    {{ form.submit }}
  </form>
 </div>

I never got the "Form posted" string even when I write something in the Name field.

Thanks in advance,

Thill answered 3/1, 2014 at 13:52 Comment(0)
Z
16

You have to initialize the form instance with values from the request:

from flask import request

@app.route('/contact', methods=['GET','POST'])
def contact():
    form = ContactForm(request.form)
    if request.method == "POST" and form.validate():
        # do something with form
        # and probably return a redirect
    return render_template("contact.html", form=form)

Here's a better tutorial than the one you link in your question: http://flask.pocoo.org/docs/patterns/wtforms/.

Have a look at the template rendering code in the tutorial, make sure you render the form field errors. If the form is posted but does not validate the code will fall through to render_template with the form instance containing field validation errors (again, see the tutorial and WTForms documentation for details).

Zoniazoning answered 3/1, 2014 at 14:2 Comment(1)
This no longer fixes the issue. See the fix below related to CSRF token.Firstfoot
Q
39

I always fail the form.validate_on_submit() when I am testing the login form following the demo code in Miguel Grinberg's book "Flask Web Development". So I think I should find a way to debug.

The debug approach I am taking is adding the code below to the app/auth/views.py:

flash(form.errors)

Then it shows me the culprit when I run to the login page:

errors={'csrf_token': ['CSRF token missing']}

So I recommend to use form.errors message to debug.

Quest answered 2/1, 2016 at 20:41 Comment(4)
It was the error in my case. I was using form.hidden_field instead of form.hidden_tag().Resume
You save my lifeMeldon
I've just started developing with Flask, and I have no clue how to debug. Thanks for this, and here's an example usage: https://mcmap.net/q/742703/-flask-wtfform-flash-does-not-display-errorsStumer
with your note I could get the error: The CSRF tokens do not match . I have searched it on the web but they did not worked for me, Do you have any idea about this issue? I have set the csrf in the form through {{ form.hidden_tag() }} and {{ form.csrf_token }}Balliol
Z
16

You have to initialize the form instance with values from the request:

from flask import request

@app.route('/contact', methods=['GET','POST'])
def contact():
    form = ContactForm(request.form)
    if request.method == "POST" and form.validate():
        # do something with form
        # and probably return a redirect
    return render_template("contact.html", form=form)

Here's a better tutorial than the one you link in your question: http://flask.pocoo.org/docs/patterns/wtforms/.

Have a look at the template rendering code in the tutorial, make sure you render the form field errors. If the form is posted but does not validate the code will fall through to render_template with the form instance containing field validation errors (again, see the tutorial and WTForms documentation for details).

Zoniazoning answered 3/1, 2014 at 14:2 Comment(1)
This no longer fixes the issue. See the fix below related to CSRF token.Firstfoot
N
12

Just encountered the issue, and the solution was to add hidden_tag right under the form in the template:

...
<form action="{{ url_for('contact') }}" method=post>
{{ form.hidden_tag() }}
...
Neumeyer answered 18/3, 2017 at 12:24 Comment(1)
@SARose This works because otherwise happens this: https://mcmap.net/q/685993/-flask-wtforms-validation-always-false – the validation fails due to the missing CSRF token. So to fix it we need to insert the CSRF token in the form as a hidden input value. So {{ form.hidden_tag() }} in fact inserts something like this into the HTML: <input type="hidden" name="csrf-field-name" value="csrf-token-here">. The csrf-token now gets sent with the form and the validation passes.Neumeyer
E
5

As @Paul Vergeev, just add:

<form action="{{ url_for('contact') }}" method=post>
{{ form.csrf_token }}

This is required because your form includes validators hence adding an 'input' of type "Hidden" in your html form. Without this hidden token in your html, the validator cannot validate your users' inputs and the result of validation will always be False.

{{ form.hidden_tag() }}

will include all hidden inputs fields in your html code; but they will not be displayed on the page.

{{ form.csrf_token }}

will include just the hidden csrf_token input field in your html code; but they will not be displayed on the page.

One more thing to do: you must configure your app's SECRET_KEY. Do this by including app.config["SECRET_KEY"] = "a secret key you won't forget" just below the app initialization i.e. just after app = Flask(__name__).

All this is a way for WTForms validators to protect your site from CSRF(often pronounced as c-surf). You can also read more about CSRF here.

Eparchy answered 22/4, 2017 at 3:16 Comment(1)
Thank you @John Mutuma ... This saved my time :)Malanie

© 2022 - 2024 — McMap. All rights reserved.