Flask wtf form AttributeError: 'Request' object has no attribute 'POST'

ciacicode picture ciacicode · Apr 7, 2015 · Viewed 16.4k times · Source

I just started coding with Flask and I want to set up CSRF on a small form in my application. I am following this https://wtforms.readthedocs.io/en/stable/csrf.html for session based implementation. I looked around the web for a while for a solution to a similar problem but I had no success, apologies if this is a duplicate question.

Problem with this code: When I run it in the virtual environment I get AttributeError: 'Request' object has no attribute 'POST' -

Goal: implement csrf on wtform instance

Environment: wtf version 2.02, flask 0.10, venv with python 2.7

fci_form.py

from flask import session, request
from flask.ext.wtf import Form
from wtforms import TextField, validators, SubmitField
from wtforms.validators import Required, Length
from wtforms.csrf.session import SessionCSRF
from datetime import timedelta
import config # my config file

# create super class

class MyForm(Form):
    class Meta:
        csrf = True
        csrf_class = SessionCSRF
        csrf_secret = config.secret_key
        csrf_time_limit = timedelta(minutes=20)

        @property
            def csrf_context(self):
                return request.session


# create a class for the form
class postcodeInput(MyForm):
    postcode = TextField('postcode',[validators.Required(message=(u"Where is your postcode?")),validators.Length(min=2, max=10)])
    submit = SubmitField('Submit')

views.py

from flask import Flask, render_template, request, __version__, url_for, session, abort, flash, redirect
# importing the class called postcode_input
from fci_form import postcodeInput
import config
import fciUtils

#pdb.set_trace()
app = Flask(__name__)
app.debug = True

# Views of the app

@app.route('/')
def index():
  return render_template('home.html')


@app.route('/fci', methods=['GET', 'POST'])
def fci_form():
    error = None
    form = postcodeInput(request.POST, meta={'csrf_context': request.session})
    if form.validate_on_submit():
            # handle user input
            postcode = request.form['postcode']
            # calculate fci
            result = fciUtils.fciReturn(postcode)
            return render_template('fci_form.html',form = form, result = result)
    elif request.method == 'GET':
        return render_template('fci_form.html', form = form)
    else:
        error = 'Enter a valid postcode'
        return render_template('fci_form.html', form=form, error=error)



if __name__ == '__main__':
    app.secret_key = config.secret_key
    app.run(threaded=True)

The template is fci_form.html in the/templates folder

{% extends 'layout.html' %}
{% block form %}
<section>
    <header><h1>...</h1><br></header>
    <form action="{{ url_for('fci_form')}}" method='POST'>
        <p>Enter your London postcode:</p>
        <section>
            {% if error %}
            <p class="error"><strong>Error: </strong>{{error}}</p>
            {% endif %}
            {{form.csrf_token}}
            {{form.postcode}}
            {{form.submit(class_="pure-button")}}   
        </section>
    </form> 
</section> 

{% endblock %}

{% block result %}

    <section>
        <h4>{{result}}</h4> 
    </section>

{% endblock %}

What am I missing here?

Answer

SuperShoot picture SuperShoot · Aug 27, 2019

From the github README for the WTForms project:

WTForms is a flexible forms validation and rendering library for Python web development. It is framework agnostic and can work with whatever web framework and template engine you choose.

..emphasis mine. Framework agnostic means that this isn't just a library for Flask and that examples such as these (from https://wtforms.readthedocs.io/en/stable/csrf.html#using-csrf):

def view():
    form = UserForm(request.POST)
    if request.POST and form.validate():
        pass # Form is valid and CSRF succeeded

    return render('user.html', form=form)

... aren't necessarily a working pattern in any web framework, rather just a general illustration to exhibit how the library works.

That example transposed to a Flask specific example might look something like this:

@app.route('/submit', methods=("GET", "POST"))
def submit():
    form = UserForm(request.form)
    if request.method == "POST" and form.validate():
        pass  # Form is valid and CSRF succeeded
    return render_template('submit.html', form=form)

The README goes on to say:

There are various community libraries that provide closer integration with popular frameworks.

One such example is Flask-WTF, and the 'hello world' for their library around WTForms looks like this:

@app.route('/submit', methods=('GET', 'POST'))
def submit():
    form = MyForm()
    if form.validate_on_submit():
        return redirect('/success')
    return render_template('submit.html', form=form)

Notice that request.form doesn't have to be passed to the MyForm constructor as it did in the vanilla WTForms example (UserForm(request.form)), and that there is a method available on the forms called validate_on_submit() which both tests that the request is a POST request and also that the submitted form content passes validation.

Along with easier handling of passing POST data through to forms, and validation, Flask-WTF also simplifies CSRF token management, which can be read about here.