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_KEY is required for CSRF protection — always store it as an environment variable in production.
  • NameForm defines 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 populates form.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.