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
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
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
| Feldklasse | HTML-Tag | Beschreibung |
|---|---|---|
| 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
| Validator | Funktion |
|---|---|
| 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
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
{% 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
| Aspekt | Manuelles request.form | Flask-WTF |
|---|---|---|
| Codezeilen | Für jedes Feld die drei Schritte wiederholen: abrufen/validieren/melden | Formularklasse einmal definieren |
| CSRF-Schutz | Manuelle Implementierung erforderlich | Automatisch |
| Benutzerdefinierte Validierung | Manuelles if/else | validate_xxx-Methode |
| Fehleranzeige | Manuelle Verkettung | Automatische Sammlung von form.field.errors |
| Anwendbare Szenarien | Einfache 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