Formularverarbeitung — Flask-WTF

In diesem Kapitel lernst du, Flask-WTF zu verwenden, um die Formularverarbeitung zu standardisieren: Definieren von Formularklassen, automatischer CSRF-Schutz, Feldvalidierung und Fehlermeldungen.


Warum Flask-WTF verwenden?

Die Registrierungs- und Login-Formulare im vorherigen Kapitel wurden von Hand erstellt: manuell ausrequest.form.get()Werte abrufen, manuelle Validierung, manuelles Anzeigen von Fehlern.

Flask-WTFEin Wrapper um WTForms für Flask, der Folgendes bietet:

  • Definition von Formularklassen, zentrale Verwaltung von Feldern + Validierungsregeln.
  • Automatischer CSRF-Schutz
  • Felder und Fehlermeldungen in Templates rendern
  • form.validate_on_submit()Einheitliche Behandlung von GET/POST-Bestimmung und Validierung.

Installation und Konfiguration

(venv) $ pip install flask-wtf

SECRET_KEY in app.py konfigurieren (CSRF-Token hängt von diesem Schlüssel ab):

Beispiel

# Dateipfad: app.py
app.config['SECRET_KEY'] = 'your-secret-key'    # Umgebungsvariablen für die Produktionsumgebung verwenden
app.config['WTF_CSRF_ENABLED'] = True            # Standardmäßig aktiviert, explizit deklarieren

Formularklasse definieren

Beispiel

# Dateipfad: app/forms.py (neue Datei)
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, EmailField, BooleanField
from wtforms.validators import DataRequired, Length, Email, EqualTo, ValidationError
from app.models import User

class RegisterForm(FlaskForm):
    Registrierungsformular
    username = StringField(Benutzername, validators=[
        DataRequired(message=Benutzername darf nicht leer sein),
        Length(min=3, max=50, message=Benutzername muss 3-50 Zeichen lang sein)
    ])
    email = EmailField(E-Mail, validators=[
        DataRequired(message=E-Mail darf nicht leer sein),
        Email(message=Bitte geben Sie eine gültige E-Mail-Adresse ein)
    ])
    password = PasswordField(Passwort, validators=[
        DataRequired(message=Passwort darf nicht leer sein),
        Length(min=6, message=Das Passwort muss mindestens 6 Zeichen lang sein)
    ])
    confirm_password = PasswordField(Passwort bestätigen, validators=[
        DataRequired(message=Bitte geben Sie das Passwort erneut ein),
        EqualTo('password', message=Die beiden Passwörter stimmen nicht überein)
    ])

    # Benutzerdefinierter Validator: Prüfen, ob der Benutzername bereits registriert ist
    def validate_username(self, field):
        if User.query.filter_by(username=field.data).first():
            raise ValidationError(Dieser Benutzername ist bereits vergeben.)

    def validate_email(self, field):
        if User.query.filter_by(email=field.data).first():
            raise ValidationError(Diese E-Mail ist bereits registriert.)


class LoginForm(FlaskForm):
    """Anmeldeformular"""
    username = StringField(Benutzername, validators=[
        DataRequired(message=Benutzername darf nicht leer sein)
    ])
    password = PasswordField(Passwort, validators=[
        DataRequired(message=Passwort darf nicht leer sein)
    ])
    remember = BooleanField(Angemeldet bleiben)

Gängige Feldtypen

FeldklasseHTML-TagBeschreibung
StringField<input type="text">Einzeiliger Text
PasswordField<input type="password">Passworteingabe
EmailField<input type="email">E-Mail-Eingabe
TextAreaField<textarea>Mehrzeiliger Text
BooleanField<input type="checkbox">Kontrollkästchen
SelectField<select>Dropdown-Auswahl

Gängige Validatoren

ValidatorFunktion
DataRequired()Feld darf nicht leer sein
Length(min=, max=)Begrenzung der Zeichenlänge
Email()Validierung des E-Mail-Formats
EqualTo('field_name')Entspricht dem Wert eines anderen Feldes
Optional()Feld darf leer sein (nachfolgende Validatoren überspringen).

Formulare in View-Funktionen verwenden

Beispiel

# Dateipfad: app/blueprints/auth.py (refaktorierte Registrierung und Anmeldung)
from flask import Blueprint, render_template, redirect, url_for, flash
from flask_login import login_user, logout_user, login_required, current_user
from app.forms import RegisterForm, LoginForm
from app.models import User, db

auth_bp = Blueprint('auth', __name__)

@auth_bp.route("/register", methods=['GET', 'POST'])
def register():
    if current_user.is_authenticated:
        return redirect(url_for('main.index'))

    form = RegisterForm()
    # form.validate_on_submit() validiert das Formular bei POST-Anfragen
    # Gibt bei GET-Anfragen oder bei fehlgeschlagener Validierung False zurück
    if form.validate_on_submit():
        user = User(username=form.username.data, email=form.email.data)
        user.set_password(form.password.data)
        db.session.add(user)
        db.session.commit()
        login_user(user)
        flash(fRegistrierung erfolgreich, willkommen, {user.username}!, 'success')
        return redirect(url_for('main.index'))

    # Bei GET-Anfragen oder fehlgeschlagener Validierung das Template mit dem Formular rendern
    return render_template('register.html', form=form)


@auth_bp.route("/login", methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return redirect(url_for('main.index'))

    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        if user is None or not user.check_password(form.password.data):
            flash(Falscher Benutzername oder falsches Passwort., 'error')
            return render_template('login.html', form=form)

        login_user(user, remember=form.remember.data)
        flash(fWillkommen zurück, {user.username}!, 'success')
        next_page = request.args.get('next')
        return redirect(next_page or url_for('main.index'))

    return render_template('login.html', form=form)

Formulare in Templates rendern

Beispiel

<!-- Dateipfad: app/templates/register.html -->
{% extends 'base.html' %}

{% block title %}Registrieren - EXAMPLE Blog{% endblock %}

{% block content %}
<div class="auth-form">
    <h2>Register</h2>
    <form method="post">
        <!-- form.hidden_tag() generiert automatisch das versteckte CSRF-Token-Feld -->
        {{ form.hidden_tag() }}

        <div class="form-group">
            {{ form.username.label }}
            {{ form.username(class="form-input") }}
            {% if form.username.errors %}
                {% for error in form.username.errors %}
                <span class="field-error">{{ error }}</span>
                {% endfor %}
            {% endif %}
        </div>

        <div class="form-group">
            {{ form.email.label }}
            {{ form.email(class="form-input") }}
            {% for error in form.email.errors %}
            <span class="field-error">{{ error }}</span>
            {% endfor %}
        </div>

        <div class="form-group">
            {{ form.password.label }}
            {{ form.password(class="form-input") }}
            {% for error in form.password.errors %}
            <span class="field-error">{{ error }}</span>
            {% endfor %}
        </div>

        <div class="form-group">
            {{ form.confirm_password.label }}
            {{ form.confirm_password(class="form-input") }}
            {% for error in form.confirm_password.errors %}
            <span class="field-error">{{ error }}</span>
            {% endfor %}
        </div>

        {{ form.submit(class="btn-submit") }}
    </Form>
    <p class="form-footer">
Hast du bereits ein Konto?<a href="{{ url_for('auth.login') }}">Jetzt anmelden</a>
    </p>
</div>
{% endblock %}

Der CSRF-Schutz von Flask-WTF ist automatisch:form.hidden_tag()Erzeugt ein verstecktes<input>Enthält ein zufälliges Token; der Server validiert dieses Token, bevor er die POST-Anfrage akzeptiert. Jedes von einer externen Website übermittelte Formular wird abgelehnt, da ihm das korrekte Token fehlt.


Manueller Vergleich: request.form vs. Flask-WTF

AspektManuelles request.formFlask-WTF
CodezeilenFür jedes Feld die drei Schritte wiederholen: abrufen/validieren/meldenFormularklasse einmal definieren
CSRF-SchutzManuelle Implementierung erforderlichAutomatisch
Benutzerdefinierte ValidierungManuelles if/elsevalidate_xxx-Methode
FehleranzeigeManuelle VerkettungAutomatische Sammlung von form.field.errors
Anwendbare SzenarienEinfache Formulare (z. B. Suchfeld)Komplexe Formulare (Registrierung, Anmeldung, Profilbearbeitung)

Kapitelzusammenfassung

In diesem Kapitel haben Sie das Authentifizierungsformular mit Flask-WTF refaktorisiert: FlaskForm definiert Felder und Validatoren, validate_xxx für benutzerdefinierte Validierung, form.validate_on_submit() behandelt die POST-Validierung einheitlich, form.hidden_tag() bietet automatischen CSRF-Schutz, und Fehlermeldungen werden in Templates gerendert.

Die Formularverarbeitung ist jetzt standardisierter und sicherer.

Weitere Erweiterungen