Python Docs
Forms & Validation
Web forms are a common way to collect user input. To keep them secure and reliable, you must handle CSRF protection and perform server-side validation. Flask-WTF and Django Forms provide structured ways to do this safely.
Why Forms & Validation Matter
- Prevent invalid or malicious data from entering your system.
- Show meaningful error messages to users.
- Defend against CSRF (Cross-Site Request Forgery) attacks.
- Centralize validation rules instead of scattering logic.
Flask-WTF (Flask + WTForms)
Flask-WTF integrates WTForms with Flask, adding CSRF protection and helper methods for validating and rendering forms.
Install
pip install flask-wtf
Example: Flask Form with Validation
# app.py
from flask import Flask, render_template, redirect
from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import DataRequired, Length
app = Flask(__name__)
app.config['SECRET_KEY'] = 'dev-secret' # use env var in production
class NameForm(FlaskForm):
name = StringField('Name', validators=[DataRequired(), Length(max=50)])
@app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
return redirect('/ok')
return render_template('form.html', form=form)<!-- templates/form.html -->
<form method="post">
{{ form.hidden_tag() }}
{{ form.name.label }} {{ form.name() }}
<button type="submit">Submit</button>
{% for f, errs in form.errors.items() %}
{% for e in errs %}<p style="color:red">{{ e }}</p>{% endfor %}
{% endfor %}
</form>Key points in this example:
SECRET_KEYis required for CSRF protection — always store it as an environment variable in production.NameFormdefines a field with validators:DataRequired()— field must not be empty.Length(max=50)— prevents overly long input.
validate_on_submit()checks both method (POST) and validation rules.form.hidden_tag()renders a hidden CSRF token automatically.- Errors are looped and displayed in red for better UX.
Django Forms
Django has built-in form handling through forms.Form and forms.ModelForm. Forms automatically handle:
- HTML rendering of input fields.
- Server-side validation & error messages.
- CSRF protection (when using Django templates).
- Binding POST data to Python objects.
Example: Django Form
# forms.py
from django import forms
class NameForm(forms.Form):
name = forms.CharField(max_length=50)# views.py
from django.shortcuts import render, redirect
from .forms import NameForm
def index(request):
if request.method == 'POST':
form = NameForm(request.POST)
if form.is_valid():
# use form.cleaned_data['name']
return redirect('ok')
else:
form = NameForm()
return render(request, 'index.html', {'form': form})<!-- templates/index.html -->
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>What Django provides automatically:
{% csrf_token %}tag inserts a hidden CSRF token.form.is_valid()applies validation rules and populatesform.errors.- Cleaned and validated data is accessible via
form.cleaned_data. form.as_p()quickly renders all fields wrapped in<p>tags.
Best Practices for Forms & Validation
- Always validate data on the server (never trust only client-side JS).
- Use CSRF protection on all forms that modify data.
- Limit field lengths (
max_length,Length(max=...)) to avoid abuse. - Show clear error messages near the inputs.
- Centralize validation logic in forms or schemas, not ad-hoc in views.