Flask Templates – Jinja2

6 Kernkonzepte
Die wichtigsten Jinja2-Template-Konzepte für Flask: {{ }} · {% %} · Vererbung · Filter · Makros · url_for Jinja2 ist die Template-Engine von Flask. Sie ermöglicht die dynamische Generierung von HTML-Seiten mit Variablen, Kontrollstrukturen, Vererbung, Filtern und Makros – für saubere und wartbare Templates.

Grundlagen-Syntax – Variablen & Kontrollstrukturen

{{ }} · {% %} · {# #}
{{ variable }} # Variablen-Ausgabe {% if condition %} ... {% endif %} # Bedingung {% for item in items %} ... {% endfor %} # Schleife {# Kommentar #} # Template-Kommentar

Jinja2-Syntax verwendet geschweifte Klammern für die Ausgabe von Variablen ({{ }}), Prozentzeichen für Logik ({% %}) und Kommentare ({# #}).

Beispiele
<!-- Variablen -->
<h1>Hallo {{ name }}!</h1>
<p>Alter: {{ age }}</p>
<!-- Attribute zugreifen -->
<p>{{ user.name }} – {{ user['email'] }}</p>
<!-- Bedingung -->
{% if user.logged_in %}
<p>Willkommen zurück!</p>
{% else %}
<a href="{{ url_for('login') }}">Login</a>
{% endif %}
<!-- Schleife -->
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
<!-- if-elif-else -->
{% if score >= 90 %}
<p>Sehr gut</p>
{% elif score >= 70 %}
<p>Gut</p>
{% else %}
<p>Verbesserungswürdig</p>
{% endif %}
Tipp: Verwenden Sie {{ variable | default('Standard') }}, um einen Fallback-Wert für fehlende Variablen zu setzen.

Template-Vererbung – Base-Layout & Blocks

extends · block · super
<!-- base.html --> {% block title %}{% endblock %} {% block content %}{% endblock %} <!-- child.html --> {% extends 'base.html' %} {% block title %}Startseite{% endblock %}

Template-Vererbung ermöglicht die Wiederverwendung von Layouts. Ein Basis-Template definiert Blöcke ({% block %}), die in Kind-Templates überschrieben werden können.

Beispiele
<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Meine App{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<header>
{% block header %}<h1>Meine App</h1>{% endblock %}
</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>
{% block footer %}© 2025 Meine App{% endblock %}
</footer>
</body>
</html>
<!-- templates/index.html -->
{% extends "base.html" %}
{% block title %}Startseite{% endblock %}
{% block content %}
<h2>Willkommen auf der Startseite</h2>
<p>Hier steht der Inhalt der Startseite.</p>
{% endblock %}
<!-- super() verwendet – Eltern-Inhalt einbinden -->
{% block footer %}
{{ super() }} <!-- Footer vom Base-Template -->
<p>Zusätzlicher Footer-Text</p>
{% endblock %}
Tipp: Verwenden Sie {{ super() }} in einem Block, um den Inhalt des Eltern-Blocks zu übernehmen und zu erweitern – ideal für Footer oder Header-Ergänzungen.

Filter – Daten transformieren

| · safe · default · length · upper · lower · join · truncate
{{ value | default('Standard') }} {{ text | truncate(50) }} {{ html_content | safe }}

Filter transformieren Werte vor der Ausgabe – sie werden mit dem Pipe-Symbol (|) angewendet und können verkettet werden.

Wichtige Filter

Filter Beschreibung Beispiel
default Fallback-Wert bei fehlender Variable {{ name | default('Gast') }}
safe HTML nicht escapen (Vorsicht!) {{ html | safe }}
length Länge einer Sequenz {{ items | length }}
upper / lower Groß-/Kleinschreibung {{ text | upper }}
capitalize Erster Buchstabe groß {{ 'hallo' | capitalize }}
title Jedes Wort groß {{ 'hallo welt' | title }}
truncate Text kürzen {{ text | truncate(50) }}
join Liste zu String verbinden {{ list | join(', ') }}
replace Text ersetzen {{ text | replace('a', 'b') }}
round Zahl runden {{ 3.14159 | round(2) }}
first / last Erstes/letztes Element {{ items | first }}
Beispiele
<!-- Filter-Verkettung -->
{{ text | upper | truncate(30) }}
<!-- Listen mit join -->
<p>Tags: {{ tags | join(' • ') }}</p>
<!-- Datum formatieren (custom Filter) -->
# In app.py: @app.template_filter('date')
{{ created_at | date }}
<!-- HTML sicher ausgeben (nur bei vertrauenswürdigem Inhalt!) -->
{{ content | safe }}
Tipp: Verwenden Sie | safe nur bei Inhalten, die Sie vertrauen – andernfalls besteht die Gefahr von XSS-Angriffen. Für Datumsformatierungen können Sie eigene Filter mit @app.template_filter() erstellen.

Makros & Include – Wiederverwendbare Komponenten

macro · import · include
<!-- macros.html --> {% macro button(label, url='#') %} <a href="{{ url }}" class="btn">{{ label }}</a> {% endmacro %} <!-- Verwendung --> {% from 'macros.html' import button %} {{ button('Speichern', '/save') }}

Makros sind wiederverwendbare Template-Fragmente ähnlich wie Funktionen. Include bindet den Inhalt einer anderen Template-Datei ein.

Beispiele
<!-- templates/macros/forms.html -->
{% macro input_field(name, label, type='text', value='') %}
<div class="field">
<label for="{{ name }}">{{ label }}</label>
<input type="{{ type }}" id="{{ name }}" name="{{ name }}" value="{{ value }}">
</div>
{% endmacro %}
<!-- templates/macros/cards.html -->
{% macro card(title, content, footer=None) %}
<div class="card">
<h3>{{ title }}</h3>
<p>{{ content }}</p>
{% if footer %}
<div class="card-footer">{{ footer }}</div>
{% endif %}
</div>
{% endmacro %}
<!-- Verwendung in einer Seite -->
{% from 'macros/forms.html' import input_field %}
{% from 'macros/cards.html' import card %}
<form>
{{ input_field('email', 'E-Mail', 'email') }}
{{ input_field('password', 'Passwort', 'password') }}
</form>
{{ card('Titel', 'Inhalt der Karte', 'Footer-Text') }}
<!-- Include – Datei einbinden -->
{% include 'includes/navigation.html' %}
Tipp: Makros sind ideal für Formular-Elemente, Buttons oder Karten. include eignet sich für wiederkehrende Code-Blöcke wie Navigation oder Footer, die keine Parameter benötigen.

Helfer-Funktionen – url_for, with, set

url_for · with · set · do
{{ url_for('index') }} {{ url_for('user', id=42) }} {% with messages = get_flashed_messages() %} {% set active_page = 'home' %}

Helfer-Funktionen erleichtern die Arbeit mit URLs, Variablen und Flashed Messages.

Beispiele
<!-- url_for – URLs generieren -->
<a href="{{ url_for('index') }}">Startseite</a>
<a href="{{ url_for('user_profile', username='anna') }}">Profil</a>
<!-- Static Files -->
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<img src="{{ url_for('static', filename='images/logo.png') }}" alt="Logo">
<!-- with – Variablen im lokalen Scope -->
{% with user_name = user.first_name + ' ' + user.last_name %}
<p>Willkommen, {{ user_name }}!</p>
{% endwith %}
<!-- set – Variable definieren -->
{% set active = 'home' %}
<li class="{% if active == 'home' %}active{% endif %}">
<a href="{{ url_for('home') }}">Home</a>
</li>
<!-- Flash Messages mit with -->
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class="flashes">
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<!-- do – Ausführen ohne Ausgabe -->
{% do analytics.track('page_view') %}
Tipp: url_for() ist die empfohlene Methode für Links in Flask-Templates – bei Änderungen der Routen müssen Sie keine Links manuell anpassen.

Fortgeschrittene Konzepte – Custom Filter, Globals, Tests

@app.template_filter · @app.context_processor
# Custom Filter @app.template_filter('date') def format_date(value, format='%d.%m.%Y'): return value.strftime(format) # Context Processor (globale Variablen) @app.context_processor def inject_user(): return { 'current_user': get_user() }

Fortgeschrittene Konzepte erweitern Jinja2 um benutzerdefinierte Filter, globale Variablen und Tests – für mehr Flexibilität und Wiederverwendbarkeit.

Beispiele
# app.py – Custom Filter
from datetime import datetime
@app.template_filter('time_ago')
def time_ago(dt):
now = datetime.utcnow()
diff = now - dt
if diff.days > 7:
return dt.strftime('%d.%m.%Y')
elif diff.days > 0:
return f'vor {diff.days} Tagen'
elif diff.seconds > 3600:
return f'vor {diff.seconds // 3600} Stunden'
elif diff.seconds > 60:
return f'vor {diff.seconds // 60} Minuten'
else:
return 'gerade eben'
# In Template: {{ post.created_at | time_ago }}
# app.py – Context Processor (globale Variablen)
@app.context_processor
def inject_globals():
return {
'app_name': 'Meine Flask App',
'year': datetime.now().year,
'is_production': not app.config['DEBUG']
}
# In jedem Template verfügbar: {{ app_name }}, {{ year }}
# app.py – Custom Test
@app.template_test('even')
def is_even(n):
return n % 2 == 0
# In Template: {% if number is even %}Gerade{% else %}Ungerade{% endif %}
Tipp: Context Processor sind ideal für global verfügbare Variablen wie App-Name, aktuelle Jahreszahl oder Benutzerinformationen – sie sparen Code in jedem Template.

Jinja2 im Überblick

{{ }} Variablen ausgeben
Text oder HTML
{% %} Logik & Kontrollstrukturen
if, for, block
| filter Daten transformieren
upper, truncate, safe
{% block %} Template-Vererbung
Wiederverwendbare Layouts
macro Wiederverwendbare Komponenten
Wie Funktionen
url_for URLs generieren
Routen-Änderungen sicher

Quick Summary

{{ }}
Variablen
{% block %}
Vererbung
| filter
Filter
macro
Makros
url_for
Helfer
custom
Erweitern
{{ user.name | upper }} · {% if logged_in %} ... {% endif %} · {% block content %}{% endblock %}