Flask-WTF - validate_on_submit() is never executed

49,174

Solution 1

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.

Solution 2

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.

Solution 3

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>

Solution 4

insert this after the tag in template html file:

 {{ form.csrf_token }} 

Solution 5

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'))
Share:
49,174
kadrian
Author by

kadrian

Full-stack web-hipster, UX lover, looking for machine learning, optimization and UI design. Useful stuff. Tokyo. Berlin.

Updated on July 09, 2022

Comments

  • kadrian
    kadrian almost 2 years

    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?

  • kadrian
    kadrian almost 12 years
    thanks for this hint, but 'print form.errors' prints {} . Still form.validate() returns False
  • kadrian
    kadrian almost 12 years
    Thank you so much! This was the exact problem! I didn't include the csrf field in my form. Trying to solve it with {{ form.csrf }} didn't work, but doing: {{ form.hidden_tag() }} solved the problem! You were also right about the error printing, good to know how that works properly now!
  • A.Ford
    A.Ford almost 12 years
    Glad to help. If you're running WTForms 0.6 or later, form.csrf became form.csrf_token so watch out for that, but form.hidden_tag() works just as well.
  • skjoshi
    skjoshi about 9 years
    This was giving me nightmare. Thanks a lot.
  • roymustang86
    roymustang86 about 7 years
    Wonder why this critical part is missing from the docs. Thanks!
  • jxramos
    jxramos almost 7 years
    I was wondering how one would go about debugging Flask form validation problems. Very good