Python Docs
Working with Forms
Web forms are the main way users send data to your application. To keep your app secure and reliable, you must handle form submissions with proper server-side validation, CSRF protection, and clean error handling. Flask supports both simple manual form handling and more structured validation with Flask-WTF.
Forms Basics
A typical flow for handling a form submission is:
- User loads a page with an HTML form.
- User fills fields and submits a
POSTrequest. - Server validates and processes the data.
- Server responds with a success page or error messages.
Flask Forms (Manual Handling)
With plain Flask, you can read form fields directly from request.form. This is fine for simple cases, but you must write your own validation.
Example: Simple Contact Form (Flask)
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/contact', methods=['GET', 'POST'])
def contact():
if request.method == 'POST':
name = request.form.get('name')
email = request.form.get('email')
# Process data
return f'Thanks {name}!'
return render_template('contact.html')<!-- contact.html --> <form method="post"> <input name="name" required> <input type="email" name="email" required> <button type="submit">Submit</button> </form>
What this simple example does:
methods=['GET', 'POST']means the same route serves both the form and its submission.- On
GET, it renderscontact.html. - On
POST, it grabs data fromrequest.formand returns a response. - HTML uses
requiredattributes for basic client-side validation, but this is not enough by itself.
Flask-WTF (Forms & Validation)
For anything non-trivial, use Flask-WTF. It wraps WTForms and gives you:
- Server-side validation with reusable form classes.
- Automatic CSRF protection.
- Field definitions and validators in one place.
Install Flask-WTF
pip install flask-wtf
Example: Validated Form with Flask-WTF
from flask_wtf import FlaskForm
from wtforms import StringField, EmailField
from wtforms.validators import DataRequired, Email
class ContactForm(FlaskForm):
name = StringField('Name', validators=[DataRequired()])
email = EmailField('Email', validators=[DataRequired(), Email()])
@app.route('/contact', methods=['GET', 'POST'])
def contact():
form = ContactForm()
if form.validate_on_submit():
# form.name.data, form.email.data
return 'Valid!'
return render_template('contact.html', form=form)What Flask-WTF is doing for you:
ContactFormdefines fields and validation rules in one place.DataRequired()ensures the field is not empty.Email()checks that the value looks like a valid email.validate_on_submit()checks that the request isPOSTand the form is valid.- In the template, you can render fields and show errors (e.g.
form.name.errors).
Tips
- Always validate on the server: client-side checks can be bypassed.
- Use CSRF protection (Flask-WTF’s CSRF token or framework built-in).
- Sanitize user input if you render it back to HTML to avoid XSS (or rely on framework auto-escaping).
- Keep validation logic in form classes or schemas, not scattered across views.