Flask – 02 Jinja Templating Grundlagen
Voraussetzungen
Bevor du mit diesem Modul startest, solltest du bereits vertraut sein mit:
- Python – 01 Kommentare Variablen Strings - Variablen, Strings und grundlegende Datentypen
- Python – 02 Rechnen mit Integer und Floats - Rechnen mit Zahlen in Python
- Python – 03 Wahrheitswerte und Kontrollstrukturen - Bedingungen und if-else Anweisungen
- Flask – 01 Erste Schritte - Flask-Grundlagen und erste Routen
Lernpfad-Hinweis
Dieses Modul baut auf Flask – 01 Erste Schritte auf und nutzt Python – 03 Wahrheitswerte und Kontrollstrukturen Konzepte. Wenn du dem empfohlenen Lernpfad folgst, hast du diese Themen bereits durchgearbeitet!
Was ist Jinja?
Jinja ist eine Template-Engine für Python. Diese ermöglicht es dir, HTML-Seiten dynamisch zu gestalten. Statt für jede mögliche Variante einer Webseite eine eigene HTML-Datei zu schreiben, nutzt du Templates mit Platzhaltern, die zur Laufzeit mit echten Daten gefüllt werden.
Beispiel:
- Statt 100 HTML-Seiten für 100 verschiedene Benutzer zu schreiben, erstellst du ein Template und füllst es mit den jeweiligen Benutzerdaten.
Warum Templating?
- Wiederverwendbarkeit: Ein Template für viele verschiedene Inhalte
- Wartbarkeit: Änderungen nur an einer Stelle nötig
- Dynamik: Inhalte können sich zur Laufzeit ändern
- Trennung: HTML (Darstellung) getrennt von Python (Logik)
Die Flask_Tutorial_1 Vorlage
In diesem Tutorial bauen wir auf der Flask_Tutorial_1.zip Vorlage auf. Diese enthält bereits:
- Eine einfache Flask-App (
app.py) - Ein Basis-Template (
base.html) - Eine Home-Seite (
home.html) - Ein Stylesheet (
style.css)
Schauen wir uns an, wie Jinja hier funktioniert!
Variablen an Templates übergeben
Grundprinzip
In Flask übergibt man Variablen mit der Funktion render_template() an ein Template:
Grundgerüst
1 2 3 4 5 6 7 8 9 10 11# app.py from flask import Flask, render_template app = Flask(__name__) @app.route("/") def hello_world(): name = "Murat" nachname = "Muster" geburtsjahr = 2007 return render_template("home.html", vor=name, nach=nachname, gj=geburtsjahr)
Erklärung:
- In Python definierst du Variablen (
name,nachname,geburtsjahr) - Mit
render_template()lädst du das Template - Die Parameter nach dem Dateinamen (
vor=name) übergeben die Werte ans Template - Im Template heißt die Variable dann
vor(nichtname!)
Variablen im Template ausgeben
Im Template nutzt du doppelte geschweifte Klammern {{ }}, um Variablen auszugeben:
| |
Ausgabe im Browser:
• Murat
• Muster
• 2007Wichtig zu wissen
{{ variable }}gibt den Wert aus- Die Variablen-Namen im Template müssen mit den Parametern in
render_template()übereinstimmen - Du kannst beliebig viele Variablen übergeben
Aufgabe 1: Eigene Variablen
- Erstelle eine neue Route
/profil - Definiere Variablen für: Name, Alter, Lieblingsfarbe
- Übergebe sie an ein neues Template
profil.html - Gib die Werte im Template aus
Template-Vererbung (extends und block)
Das Konzept
Stell dir vor, jede Webseite deiner App soll den gleichen Header, Footer und Navigation haben. Ohne Template-Vererbung müsstest du diesen Code auf jeder Seite wiederholen. Mit Jinja erstellst du ein Basis-Template und lässt andere Templates davon erben.
Das Basis-Template
| |
Die wichtigen Teile:
{% block title %}...{% endblock %}: Platzhalter für den Titel{% block content %}...{% endblock %}: Platzhalter für den Hauptinhalt- Alles andere (Header, Footer, Navigation) wird vererbt
Ein Child-Template
| |
So funktioniert’s:
{% extends "base.html" %}lädt das Basis-Template{% block title %}überschreibt den Titel-Block{% block content %}füllt den Content-Block- Header, Footer etc. kommen automatisch vom Basis-Template
Vorteile der Vererbung
- DRY-Prinzip: Don’t Repeat Yourself
- Konsistenz: Alle Seiten haben das gleiche Grundgerüst
- Wartbarkeit: Änderungen am Layout nur in base.html nötig
Styling mit CSS-Klassen
Statische Dateien einbinden
In Jinja nutzt du url_for(), um auf statische Dateien (CSS, JavaScript, Bilder) zuzugreifen:
| |
Flask sucht automatisch im static/ Ordner nach der Datei.
CSS-Klassen in Templates verwenden
| |
| |
Die CSS-Klassen funktionieren genauso wie in normalem HTML!
Bedingungen in Templates
Mit Jinja kannst du Bedingungen direkt im Template verwenden. Das ist nützlich, um unterschiedliche Inhalte je nach Situation anzuzeigen.
Einfache if-Bedingung
| |
| |
if-elif-else Bedingungen
| |
Mehrere Bedingungen kombinieren
| |
Existenz prüfen
| |
Aufgabe 2: Bedingungen üben
Erstelle eine Route /zahl, die eine Zahl als Variable übergibt.
Im Template:
- Prüfe, ob die Zahl gerade oder ungerade ist
- Zeige eine entsprechende Nachricht an
Tipp: Eine Zahl ist gerade, wenn zahl % 2 == 0
Werte zwischen Seiten übergeben
Oft möchtest du Daten von einer Seite zur nächsten weitergeben. Dafür gibt es in Webentwicklung zwei Hauptmethoden: GET und POST.
GET-Parameter (URL-Parameter)
GET-Parameter werden in der URL übertragen und sind sichtbar.
Beispiel:
| |
| |
URL-Beispiele:
http://localhost:5000/gruss→ zeigt “Hallo Gast!”http://localhost:5000/gruss?name=Max→ zeigt “Hallo Max!”http://localhost:5000/gruss?name=Anna→ zeigt “Hallo Anna!”
Wofür nutzt man GET?
- Suchfunktionen
- Filter
- Links teilen (URL kann kopiert werden)
- Daten abrufen (lesen)
POST-Formulare
POST sendet Daten verborgen (nicht in der URL). Das ist sicherer für sensible Daten.
Beispiel:
| |
| |
| |
Wichtige Teile:
<form method="POST">→ Formular nutzt POSTname="username"→ Feldname fürrequest.form.get('username')methods=['GET', 'POST']→ Route akzeptiert beide Methodenrequest.method == 'POST'→ Prüft, ob Formular abgesendet wurde
Wofür nutzt man POST?
- Login-Formulare
- Registrierung
- Daten speichern/ändern
- Sensible Informationen
GET vs POST - Der Unterschied
| Aspekt | GET | POST |
|---|---|---|
| Sichtbarkeit | Parameter in URL sichtbar | Nicht in URL sichtbar |
| Datenmenge | Begrenzt (~2000 Zeichen) | Praktisch unbegrenzt |
| Verwendung | Links, Filter, Suche | Formulare, Login, Daten speichern |
| Sicherheit | Weniger sicher (URL-Verlauf) | Sicherer (Daten im Request Body) |
| Browser-Cache | Wird gecacht | Wird nicht gecacht |
| Lesezeichen | Kann als Lesezeichen gespeichert werden | Kann nicht gespeichert werden |
| Beispiel | ?suche=Python&kategorie=Tutorial | Login-Formulare, Datei-Upload |
Faustregel
- GET: Wenn du Daten nur abrufen/lesen willst
- POST: Wenn du Daten senden/ändern/speichern willst
Eine Eselsbrücke: GET = “Gucken”, POST = “Posten/Senden”
Übungen
Übung 1: Personalisierte Begrüßung
Erstelle eine Route /willkommen, die einen Namen als GET-Parameter erhält.
Anforderungen:
- Wenn ein Name übergeben wird: “Hallo [Name]! Willkommen auf meiner Seite.”
- Wenn kein Name übergeben wird: “Hallo Gast! Bitte gib deinen Namen an.”
- Nutze
{% if %}im Template für die Bedingung - Erstelle mindestens 2 Links mit verschiedenen Namen
Tipp: Nutze request.args.get('name') ohne Standard-Wert, dann ist der Wert None wenn kein Parameter übergeben wird.
Lösung zeigen
1 2 3 4 5# app.py @app.route("/willkommen") def willkommen(): name = request.args.get('name') return render_template("willkommen_uebung.html", name=name)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21<!-- templates/willkommen_uebung.html --> {% extends "base.html" %} {% block title %}Willkommen{% endblock %} {% block content %} <h1>Willkommen!</h1> {% if name %} <p>Hallo {{ name }}! Willkommen auf meiner Seite.</p> {% else %} <p>Hallo Gast! Bitte gib deinen Namen an.</p> {% endif %} <h2>Versuche diese Links:</h2> <ul> <li><a href="/willkommen?name=Max">Grüße Max</a></li> <li><a href="/willkommen?name=Lisa">Grüße Lisa</a></li> <li><a href="/willkommen">Ohne Name</a></li> </ul> {% endblock %}
Übung 2: Login-System
Erstelle ein einfaches Login-Formular.
Anforderungen:
- Route
/loginmit GET und POST Methoden - Formular mit Feldern für Benutzername und Passwort
- Nach dem Absenden: Zeige “Willkommen [Benutzername]!”
- Bonus: Prüfe mit
{% if %}, ob der Benutzername leer ist
Hinweis: In dieser einfachen Version prüfen wir das Passwort noch nicht! Das kommt eventuell später.
Lösung zeigen
1 2 3 4 5 6 7# app.py @app.route("/login_uebung", methods=['GET', 'POST']) def login_uebung(): if request.method == 'POST': username = request.form.get('username') return render_template("login_erfolg.html", username=username) return render_template("login_formular.html")
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22<!-- templates/login_formular.html --> {% extends "base.html" %} {% block title %}Login{% endblock %} {% block content %} <h1>Login</h1> <form method="POST" action="/login_uebung"> <div> <label for="username">Benutzername:</label> <input type="text" id="username" name="username" required> </div> <div> <label for="password">Passwort:</label> <input type="password" id="password" name="password" required> </div> <button type="submit">Anmelden</button> </form> {% endblock %}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16<!-- templates/login_erfolg.html --> {% extends "base.html" %} {% block title %}Willkommen{% endblock %} {% block content %} <h1>Login erfolgreich!</h1> {% if username %} <p>Willkommen {{ username }}! Du hast dich erfolgreich angemeldet.</p> {% else %} <p>Fehler: Kein Benutzername angegeben.</p> {% endif %} <a href="/login_uebung">Zurück zum Login</a> {% endblock %}
Übung 3: GET vs POST verstehen
Erstelle zwei verschiedene Routen, um den Unterschied zu verstehen.
Anforderungen:
- Route
/suche- nutzt GET-Parameter für eine Suchanfrage- Template mit einem Link:
/suche?query=Python - Zeige die Suchanfrage im Template an
- Template mit einem Link:
- Route
/nachricht- nutzt POST für eine Nachricht- Formular mit einem Textfeld für die Nachricht
- Zeige die Nachricht nach dem Absenden an
Beobachte: Schaue dir die URL an, nachdem du das Formular abgeschickt hast. Siehst du einen Unterschied?
Lösung zeigen
1 2 3 4 5 6 7 8 9 10 11 12# app.py @app.route("/suche") def suche(): query = request.args.get('query', '') return render_template("suche.html", query=query) @app.route("/nachricht", methods=['GET', 'POST']) def nachricht(): if request.method == 'POST': nachricht_text = request.form.get('nachricht') return render_template("nachricht_anzeige.html", nachricht=nachricht_text) return render_template("nachricht_formular.html")
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21<!-- templates/suche.html --> {% extends "base.html" %} {% block content %} <h1>Suche</h1> {% if query %} <p>Du hast gesucht nach: <strong>{{ query }}</strong></p> {% else %} <p>Keine Suchanfrage angegeben.</p> {% endif %} <h2>Probiere diese Suchen:</h2> <ul> <li><a href="/suche?query=Python">Suche nach Python</a></li> <li><a href="/suche?query=Flask">Suche nach Flask</a></li> <li><a href="/suche?query=Jinja">Suche nach Jinja</a></li> </ul> <p><strong>Beachte:</strong> Die Suchanfrage ist in der URL sichtbar!</p> {% endblock %}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15<!-- templates/nachricht_formular.html --> {% extends "base.html" %} {% block content %} <h1>Nachricht senden</h1> <form method="POST" action="/nachricht"> <div> <label for="nachricht">Deine Nachricht:</label> <textarea id="nachricht" name="nachricht" rows="4" required></textarea> </div> <button type="submit">Senden</button> </form> {% endblock %}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17<!-- templates/nachricht_anzeige.html --> {% extends "base.html" %} {% block content %} <h1>Nachricht erhalten!</h1> {% if nachricht %} <p>Deine Nachricht:</p> <blockquote>{{ nachricht }}</blockquote> {% else %} <p>Keine Nachricht erhalten.</p> {% endif %} <p><strong>Beachte:</strong> Die Nachricht ist NICHT in der URL sichtbar!</p> <a href="/nachricht">Neue Nachricht senden</a> {% endblock %}
Zusammenfassung
Das Wichtigste auf einen Blick:
Variablen:
- In Python übergeben:
render_template("file.html", var=value) - Im Template ausgeben:
{{ variable }}
Template-Vererbung:
- Basis-Template erstellen mit
{% block name %}...{% endblock %} - In Child-Template erben:
{% extends "base.html" %} - Blöcke überschreiben mit gleichem Block-Namen
Bedingungen:
{% if bedingung %}...{% endif %}{% if %}...{% elif %}...{% else %}...{% endif %}- Mehrere Bedingungen:
and,or
Datenweitergabe:
- GET: Daten in URL sichtbar, für Links und Suche
request.args.get('name')- Verwendung:
/route?name=Max
- POST: Daten verborgen, für Formulare
request.form.get('name')<form method="POST">methods=['GET', 'POST']in Route nötig
Statische Dateien:
{{ url_for('static', filename='style.css') }}
Häufige Fehler vermeiden
Vergessene Imports:
from flask import requestnicht vergessen für GET/POST!
Template-Namen:
- Dateinamen in
render_template()müssen exakt stimmen - Templates müssen im
templates/Ordner liegen
- Dateinamen in
POST ohne methods:
- Bei POST-Formularen:
methods=['GET', 'POST']in@app.route()nicht vergessen
- Bei POST-Formularen:
Jinja-Syntax:
{{ }}für Ausgabe{% %}für Befehle (if, for, block, extends)- Nicht verwechseln!
Block-Namen:
- In Child-Templates müssen die Block-Namen exakt wie im Parent sein