Flask-WTF
Flask-WTF is a Flask extension (current version 1.2.2) that simplifies form handling by integrating the WTForms library. It provides robust features such as CSRF protection, form validation, file upload support, and reCAPTCHA integration. Maintained by the Pallets organization, it generally sees regular updates, though specific release cadences can vary.
Warnings
- breaking The base form class `flask_wtf.Form` was deprecated and removed. Applications should now import and inherit from `flask_wtf.FlaskForm`.
- breaking WTForms fields (e.g., `StringField`, `IntegerField`) are no longer imported from `flask_wtf`.
- gotcha CSRF protection requires `app.config['SECRET_KEY']` to be set to a strong, random string. Without it, Flask-WTF's CSRF features will not work, or forms will fail validation.
- gotcha For forms that include `FileField` for file uploads, the HTML form tag must include `enctype="multipart/form-data"` to ensure file data is correctly sent to the server.
- gotcha The `form.validate_on_submit()` method only validates if the request method is POST, PUT, PATCH, or DELETE. It will always return `False` for GET requests, which can be a common source of confusion.
Install
-
pip install Flask-WTF
Imports
- FlaskForm
from flask_wtf import FlaskForm
- StringField
from wtforms import StringField
- DataRequired
from wtforms.validators import DataRequired
Quickstart
import os
from flask import Flask, render_template, request, redirect, url_for
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('FLASK_SECRET_KEY', 'a_very_secret_key_for_dev')
class MyForm(FlaskForm):
name = StringField('Name', validators=[DataRequired()])
submit = SubmitField('Submit')
@app.route('/', methods=['GET', 'POST'])
def index():
form = MyForm()
if form.validate_on_submit():
name = form.name.data
print(f"Form submitted with name: {name}")
# In a real app, save 'name' to a database, etc.
return redirect(url_for('success', name=name))
return render_template('index.html', form=form)
@app.route('/success/<name>')
def success(name):
return f'Hello, {name}! Your form was submitted successfully.'
if __name__ == '__main__':
# Example template content (save as templates/index.html)
# <form method="POST" action="/">
# {{ form.hidden_tag() }}
# <p>
# {{ form.name.label }}
# {{ form.name(size=30) }}
# {% if form.name.errors %}
# <ul class="errors">
# {% for error in form.name.errors %}
# <li>{{ error }}</li>
# {% endfor %}
# </ul>
# {% endif %}
# </p>
# <p>{{ form.submit() }}</p>
# </form>
app.run(debug=True)