Flask – 04 Formulare und Datenverarbeitung
Formulare und Datenverarbeitung
Voraussetzungen
Bevor du mit diesem Modul startest, solltest du bereits vertraut sein mit:
- Python – 07 Funktionen und Methoden - Funktionen erstellen und nutzen
- Flask – 02 Jinja Templating Grundlagen - Variablen, Bedingungen und POST-Formulare
- Flask – 03 Jinja Schleifen und Listen - Listen in Templates anzeigen
Warum Datenvalidierung?
In den bisherigen Beispielen haben wir Formulardaten einfach angenommen und angezeigt. Aber in echten Anwendungen musst du Daten überprüfen, bevor du sie verarbeitest:
- Ist die E-Mail-Adresse gültig?
- Ist das Passwort stark genug?
- Wurden alle Pflichtfelder ausgefüllt?
- Ist das Alter eine gültige Zahl?
Warum ist das wichtig?
- Sicherheit: Verhindere schädliche Eingaben
- Datenqualität: Stelle sicher, dass Daten verwendbar sind
- Benutzererfahrung: Gib klares Feedback bei Fehlern
- Vermeidung von Abstürzen: Verhindere Fehler durch ungültige Daten
Validierungsfunktionen erstellen
Grundprinzip
Validierungsfunktionen prüfen Daten und geben True (gültig) oder False (ungültig) zurück.
| |
So funktioniert’s:
- Funktion erhält den zu prüfenden Wert
- Führt Prüfungen durch
- Gibt
True(OK) oderFalse(Fehler) zurück
Best Practice: Docstrings
Schreibe immer Docstrings (die Texte in """..."""), um zu erklären, was die Funktion prüft. Das hilft dir und anderen, den Code zu verstehen!
Beispiel: E-Mail-Validierung (erweitert)
| |
Validierung mit Fehler-Nachrichten
Manchmal möchtest du nicht nur wissen, ob etwas falsch ist, sondern warum:
| |
Verwendung:
| |
Komplexes Registrierungsformular
Lass uns ein vollständiges Registrierungsformular mit Validierung erstellen.
Python-Code (app.py)
| |
Template: Registrierungsformular
| |
Template: Erfolgsseite
| |
CSS für Fehler und Erfolg
| |
Wichtige Details
Warum value="{{ username }}"?
- Wenn das Formular Fehler enthält, bleiben die bereits eingegebenen Werte erhalten
- Der Benutzer muss nicht alles neu eingeben
- Nur beim Passwort lassen wir das Feld leer (Sicherheit!)
Warum .strip()?
- Entfernt Leerzeichen am Anfang und Ende
- Verhindert " " als gültigen Benutzernamen
Daten in Listen speichern (CRUD-Grundlagen)
Jetzt lernen wir, wie man Daten persistent (über mehrere Requests hinweg) speichert. Wir nutzen eine globale Liste (in echten Apps würdest du eine Datenbank verwenden).
Beispiel: Gästebuch
| |
| |
| |
Post-Redirect-Get Pattern
Nach einem erfolgreichen POST solltest du immer zu einer GET-Seite redirecten:
| |
Warum?
- Verhindert doppeltes Absenden beim Browser-Refresh
- Bessere User Experience
- Saubere URL nach dem Absenden
Aufgabe 1: Erweitere das Gästebuch
Füge folgende Features hinzu:
- Zeitstempel: Importiere
datetimeund speichere bei jedem Eintrag das aktuelle Datum/Zeit - Zeige Zeitstempel an: Formatiere mit
strftime()für schöne Ausgabe - E-Mail-Feld (optional): Benutzer können E-Mail angeben (Validierung nicht vergessen!)
Tipp: from datetime import datetime und datetime.now()
Umfrage-System
Lass uns ein System erstellen, bei dem mehrere Benutzer abstimmen können.
| |
| |
| |
| |
Prozentrechnung in Jinja
| |
stimmen / gesamt * 100berechnet den Prozentsatz|roundrundet auf ganze Zahlen- Das Ergebnis wird direkt als
widthim CSS genutzt!
Übungen
Übung 1: Kontaktformular mit Validierung
Erstelle eine Route /kontakt mit einem Kontaktformular.
Formular-Felder:
- Name (Pflicht, mindestens 3 Zeichen)
- E-Mail (Pflicht, muss @ und . enthalten)
- Betreff (Pflicht, mindestens 5 Zeichen)
- Nachricht (Pflicht, mindestens 20 Zeichen)
Anforderungen:
- Erstelle Validierungsfunktionen für jedes Feld
- Zeige alle Fehler gleichzeitig an (nicht nur den ersten!)
- Behalte eingegebene Werte bei Fehlern
- Nach erfolgreicher Übermittlung: Zeige Bestätigungsseite
Bonus: Speichere übermittelte Nachrichten in einer Liste und zeige sie auf einer separaten Seite /nachrichten an.
Lösung zeigen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70# app.py kontakte = [] # Globale Liste def validiere_name(name): return len(name) >= 3 def validiere_email(email): return "@" in email and "." in email def validiere_betreff(betreff): return len(betreff) >= 5 def validiere_nachricht(nachricht): return len(nachricht) >= 20 @app.route("/kontakt", methods=['GET', 'POST']) def kontakt(): fehler = [] if request.method == 'POST': name = request.form.get('name', '').strip() email = request.form.get('email', '').strip() betreff = request.form.get('betreff', '').strip() nachricht = request.form.get('nachricht', '').strip() # Validierung if not name: fehler.append("Name darf nicht leer sein") elif not validiere_name(name): fehler.append("Name muss mindestens 3 Zeichen lang sein") if not email: fehler.append("E-Mail darf nicht leer sein") elif not validiere_email(email): fehler.append("Ungültige E-Mail-Adresse") if not betreff: fehler.append("Betreff darf nicht leer sein") elif not validiere_betreff(betreff): fehler.append("Betreff muss mindestens 5 Zeichen lang sein") if not nachricht: fehler.append("Nachricht darf nicht leer sein") elif not validiere_nachricht(nachricht): fehler.append("Nachricht muss mindestens 20 Zeichen lang sein") if not fehler: # Speichern kontakte.append({ "name": name, "email": email, "betreff": betreff, "nachricht": nachricht }) return render_template("kontakt_erfolg.html", name=name) return render_template("kontakt.html", fehler=fehler, name=name, email=email, betreff=betreff, nachricht=nachricht) return render_template("kontakt.html", fehler=[]) @app.route("/nachrichten") def nachrichten(): return render_template("nachrichten.html", kontakte=kontakte)
Übung 2: To-Do-Liste (CRUD)
Erstelle eine einfache To-Do-Listen-App.
Anforderungen:
- Create: Formular zum Hinzufügen neuer To-Dos
- Read: Alle To-Dos anzeigen
- Jedes To-Do hat: Titel, Beschreibung, Status (offen/erledigt)
- Validierung: Titel muss mindestens 3 Zeichen haben
Bonus:
- Zeige Anzahl offener und erledigter To-Dos
- Markiere erledigte To-Dos visuell anders (z.B. durchgestrichen)
Lösung zeigen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42# app.py todos = [] @app.route("/todos", methods=['GET', 'POST']) def todo_liste(): fehler = [] if request.method == 'POST': titel = request.form.get('titel', '').strip() beschreibung = request.form.get('beschreibung', '').strip() # Validierung if not titel: fehler.append("Titel darf nicht leer sein") elif len(titel) < 3: fehler.append("Titel muss mindestens 3 Zeichen lang sein") if not fehler: todo = { "id": len(todos) + 1, "titel": titel, "beschreibung": beschreibung, "erledigt": False } todos.append(todo) return redirect(url_for('todo_liste')) return render_template("todos.html", todos=todos, fehler=fehler, titel=titel, beschreibung=beschreibung) # Statistiken berechnen offen = sum(1 for t in todos if not t["erledigt"]) erledigt = sum(1 for t in todos if t["erledigt"]) return render_template("todos.html", todos=todos, fehler=[], offen=offen, erledigt=erledigt)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45<!-- templates/todos.html --> {% extends "base.html" %} {% block content %} <h1>To-Do-Liste</h1> <div class="statistik"> <span>Offen: {{ offen }}</span> | <span>Erledigt: {{ erledigt }}</span> | <span>Gesamt: {{ todos|length }}</span> </div> {% if fehler %} <div class="fehler-box"> <ul> {% for f in fehler %} <li>{{ f }}</li> {% endfor %} </ul> </div> {% endif %} <form method="POST" action="/todos"> <div class="form-group"> <input type="text" name="titel" placeholder="Titel" value="{{ titel }}" required> </div> <div class="form-group"> <textarea name="beschreibung" placeholder="Beschreibung (optional)">{{ beschreibung }}</textarea> </div> <button type="submit" class="btn-primary">Hinzufügen</button> </form> <div class="todo-liste"> {% for todo in todos %} <div class="todo-item {% if todo.erledigt %}erledigt{% endif %}"> <h3>{{ todo.titel }}</h3> {% if todo.beschreibung %} <p>{{ todo.beschreibung }}</p> {% endif %} </div> {% else %} <p>Keine To-Dos. Erstelle dein erstes!</p> {% endfor %} </div> {% endblock %}
Übung 3: Quiz-App
Erstelle eine einfache Quiz-App mit mehreren Fragen.
Anforderungen:
- Erstelle eine Liste mit mindestens 3 Quiz-Fragen
- Jede Frage hat: Frage-Text, 4 Antwortmöglichkeiten, richtige Antwort
- Zeige alle Fragen in einem Formular an (Radio-Buttons)
- Nach dem Absenden: Zähle richtige Antworten und zeige Ergebnis
- Zeige welche Antworten richtig/falsch waren
Bonus: Berechne und zeige Prozent-Punktzahl
Lösung zeigen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49# app.py quiz_fragen = [ { "frage": "Was ist die Hauptstadt von Deutschland?", "optionen": ["Berlin", "München", "Hamburg", "Köln"], "richtig": "Berlin" }, { "frage": "Welche Programmiersprache nutzt Flask?", "optionen": ["JavaScript", "Python", "Java", "Ruby"], "richtig": "Python" }, { "frage": "Was bedeutet HTML?", "optionen": ["Hyper Text Markup Language", "High Tech Modern Language", "Home Tool Markup Language", "Hyperlinks and Text Markup Language"], "richtig": "Hyper Text Markup Language" } ] @app.route("/quiz", methods=['GET', 'POST']) def quiz(): if request.method == 'POST': richtig = 0 ergebnisse = [] for i, frage in enumerate(quiz_fragen): antwort = request.form.get(f'frage_{i}') ist_richtig = antwort == frage["richtig"] if ist_richtig: richtig += 1 ergebnisse.append({ "frage": frage["frage"], "deine_antwort": antwort, "richtige_antwort": frage["richtig"], "ist_richtig": ist_richtig }) prozent = round((richtig / len(quiz_fragen)) * 100) return render_template("quiz_ergebnis.html", ergebnisse=ergebnisse, richtig=richtig, gesamt=len(quiz_fragen), prozent=prozent) return render_template("quiz.html", fragen=quiz_fragen)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23<!-- templates/quiz.html --> {% extends "base.html" %} {% block content %} <h1>Quiz</h1> <form method="POST" action="/quiz"> {% for i, frage in fragen|enumerate %} <div class="quiz-frage"> <h3>{{ i + 1 }}. {{ frage.frage }}</h3> {% for option in frage.optionen %} <div class="radio-option"> <input type="radio" id="f{{ i }}_{{ loop.index }}" name="frage_{{ i }}" value="{{ option }}" required> <label for="f{{ i }}_{{ loop.index }}">{{ option }}</label> </div> {% endfor %} </div> {% endfor %} <button type="submit" class="btn-primary">Auswerten</button> </form> {% endblock %}
Zusammenfassung
Das Wichtigste auf einen Blick:
Validierungsfunktionen:
- Prüfen Daten vor der Verarbeitung
- Geben
True(gültig) oderFalse(ungültig) zurück - Können auch Fehler-Texte zurückgeben
Fehlerbehandlung:
- Sammle alle Fehler in einer Liste
- Zeige Fehler im Template mit Jinja-Schleife
- Behalte eingegebene Werte bei (außer Passwörter)
CRUD-Operationen:
- Create: Daten aus Formular in Liste speichern
- Read: Liste durchlaufen und anzeigen
- Nutze globale Liste (oder später: Datenbank)
- Post-Redirect-Get Pattern nach erfolgreicher Übermittlung
Best Practices:
| |
Nächste Schritte
Du kennst jetzt Formulare und Validierung! Im nächsten Modul:
- Abschlussprojekt - Kombiniere alles in einem großen Projekt!