Flask-WTF – validate_on_submit() is never executed

Question:

I’m using Flask-WTF:

Here is my form:

from flask.ext.wtf import Form, TextField

class BookNewForm(Form):
    name = TextField('Name')

Here is the controller:

@book.route('/book/new', methods=['GET', 'POST'])
def customers_new():
    form = BookNewForm()
    if form.is_submitted():
        print "submitted"
    if form.validate():
        print "valid"
    if form.validate_on_submit():
        flash("Successfully created a new book")
        return redirect(url_for('.books_show'))
    return render_template('views/books_new.html', form=form)

Now the problem is, if you look at my print statements, it always prints submitted, but it NEVER prints valid and validate_on_submit() is never executed. Why?

Asked By: kadrian

||

Answers:

you can print errors

print form.errors

or

app.logger.debug(form.errors)

and if you got csrf-error, you should set form.csrf_token in your template.

Answered By: kzfm

You’re not inserting the CSRF field in the HTML form.

<form method=post>
    {{ form.csrf_token }}
    {{ form.name }}
    <input type=submit>
</form>

After adding form.csrf_token to the template (docs), the form will validate as expected.

Add print(form.errors) after validating the form to see the errors that were raised. errors will be empty before validation. In this case, there is an error about missing

@book.route('/book/new_no_csrf', methods=['GET', 'POST'])
def customers_new_no_csrf():
    form = BookNewForm()
    print(form.errors)

    if form.is_submitted():
        print("submitted")

    if form.validate():
        print("valid")

    print(form.errors)

    if form.validate_on_submit():
        flash("Successfully created a new book")
        return redirect(url_for('.books_show'))

    return render_template('books_new.html', form=form)
{}
submitted
{'csrf_token': [u'CSRF token missing']}
127.0.0.1 - - [29/May/2012 02:01:08] "POST /book/new_no_csrf HTTP/1.1" 200 -
127.0.0.1 - - [29/May/2012 02:01:08] "GET /favicon.ico HTTP/1.1" 404 -

I created an example on GitHub.

Answered By: A.Ford

I was clearing the flask session if I wasn’t logged in before every request. This was causing this issue.

@main.before_request
def before_request():
    if not current_user.is_authenticated():
        # TODO clean sessions may cause CSRF missing issue
        session.clear()
        print "Session Cleared"
        return redirect(url_for('auth.login'))
Answered By: tourdownunder

insert this after the tag in template html file:

 {{ form.csrf_token }} 
Answered By: lihuanshuai

I came across this when trying to render a FormField being iterated over my FieldList in my template. I had to embed two hidden_tag elements one for the FieldList form and one for the FieldForm form, search the template comments for keyword “HIDDEN TAG”

class ParamRangeForm( FlaskForm ):
    minX = FloatField( )
    maxX = FloatField( )

class ParamRangesForm( FlaskForm ):
    paramRanges = FieldList( FormField( ParamRangeForm ) )
    submit      = SubmitField( 'Submit' )

    def loadParams( self ) :
        for paramName in ["p1" , "p2" , "p3", "p4"] :
            prf = ParamRangeForm( )
            prf.minX = -100.9#float('-inf')
            prf.maxX = 100.5#float('-inf')
            self.paramRanges.append_entry( prf )

...

  <form action="" method="POST" enctype="multipart/form-data">
    {{ rangesForm.hidden_tag() }} <!--#### HIDDEN TAG #1 -->
    <table>
      <!--Print Column Headers-->
      <thead>
      <tr>
        <th class="ColumnHeader">Parameter</td>
        <th class="ColumnHeader">Min</td>
        <th class="ColumnHeader">Max</td>
      </tr>
      </thead>

      <!--Print Parameter Rows-->
      <tbody>
      {% for paramRange in rangesForm.paramRanges %}
        <tr>
          {{ paramRange.hidden_tag() }} <!--#### HIDDEN TAG #2 -->
          <td>p{{ loop.index }}</td>
          <td>{{ paramRange.minX }}</td>
          <td>{{ paramRange.maxX }}</td>
        </tr>
      {% endfor %}
      </tbody>
    </table>
    </div>
    {{ rangesForm.submit() }}
  </form>
Answered By: jxramos

I think the API has changed.Maybe try changing

from flask.ext.wtf import Form

to:

from flask_wtf import Form
Answered By: Kanav Anand

I spent a several hours debugging a validation issue with Flask-WTF. The issue like many others was a CSRF validation issue. However, mine was not caused by any of the common issues I have found.

The standard Flask-WTF implementation of CSRF requires two things be delivered to the browser.

One: The hidden CSRF form field e.g.

<input id="csrf_token" name="csrf_token" type="hidden" value="ImYzODdmZTdhYTRlMmNkYWRjYmRlYWFmZjQxMDllZTQ1OWZmYzg3MTki.XKvOPg.gUCkF9j-vg0PrL2PRH-v43GeHu0">

Two: The session cookie HTTP response header e.g.

Set-Cookie: session=eyJjc3JmX3Rva2VuIjoiZjM4N2ZlN2FhNGUyY2RhZGNiZGVhYWZmNDEwOWVlNDU5ZmZjODcxOSJ9.XKvOPg.a3-W62MHvaGVkv2GYCi-dgpLE3Y; HttpOnly; Path=/

If either of these are missing the browser will fail to send the proper CSRF validation. Of course, this in turn causes the form validation to fail.

If the csrf_token hidden field is present in the form but the session cookie is missing, you will receive the following response when the form is submitted…

Bad Request
The CSRF session token is missing.

In my case the session cookie was missing because of a bug in my code. I needed to serve a custom HTTP header across the entire Flask site. I included it like this…

class LocalFlask(Flask):

    def process_response(self, response):
        response.headers['my-header'] = 'My Header Value'
        return response

app = LocalFlask(__name__)

This however causes anything that rellys on the the Flask.response.headers method to fail. One of those is Flaks-WTF setting the session cookie HTTP header.

This can be solved by adding the super() method to the LocalFlask class so that it inherits methods form the Flask class.

class LocalFlask(Flask):

    def process_response(self, response):
        response.headers['my-header'] = 'My Header Value'
        #LocalFlask inherits methods from Flask
        super(LocalFlask, self).process_response(response)
        return response

app = LocalFlask(__name__)
Answered By: Daniel Morell

Well I tried all the solutions mentioned

form.hidden_tag()
form.csrf_token
form.csrf 

with

app.secret_key=""
app.config["SECRET_KEY"]=""

but form.validate_on_submit() always returned false.

None of these seem to work for me, so I used the basic method and this method

import request
request.method="POST"

or

form.is_submitted()

These two worked for me

Answered By: Ashrith

In my case, form.validate_on_submit() and form.validate() always return False if I access the route via Simple Browser: Show command in vscode but return True if I use chrome

I had such problem and solved it by adding {{ form.csfr_token }} in tag :

<form method=post> 
    {{ form.csrf_token }}
    .
    .
    .
</form>

Be careful about auto suggestion which may complete your expression by this :

{{ form._csrf_token }}

The above expression is similar to the correct one but will not work. By adding the right code (which is mentioned in "form tag" validation_on_submit() method will response the right result.

Answered By: Farzin Pourabdolahi