Flask – 03 Jinja Schleifen und Listen

Flask – 03 Jinja Schleifen und Listen

Voraussetzungen

Bevor du mit diesem Modul startest, solltest du bereits vertraut sein mit:

Warum Schleifen in Templates?

In den meisten Webanwendungen zeigst du Listen von Daten an:

  • Eine Liste von Produkten in einem Shop
  • Eine To-Do-Liste
  • Blog-Beiträge
  • Suchergebnisse
  • Benutzerprofile

Statt für jeden Eintrag den gleichen HTML-Code zu wiederholen, nutzt du For-Schleifen in Jinja, um Listen dynamisch zu durchlaufen und anzuzeigen.

Das Prinzip

  • In Python erstellst du eine Liste mit Daten
  • Du übergibst die Liste ans Template
  • Im Template durchläufst du die Liste mit einer {% for %} Schleife
  • Jinja generiert automatisch HTML für jeden Eintrag

For-Schleifen in Jinja - Die Grundlagen

Einfache Liste durchlaufen

Python-Code:

1
2
3
4
5
6
7
8
9
# app.py
from flask import Flask, render_template

app = Flask(__name__)

@app.route("/produkte")
def produkte():
    produkte = ["Apfel", "Birne", "Banane", "Orange", "Traube"]
    return render_template("produkte.html", produkte=produkte)

Template-Code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<!-- templates/produkte.html -->
{% extends "base.html" %}

{% block title %}Produktliste{% endblock %}

{% block content %}
<h1>Unsere Produkte</h1>

<ul>
{% for produkt in produkte %}
    <li>{{ produkt }}</li>
{% endfor %}
</ul>
{% endblock %}

Ausgabe im Browser:

Unsere Produkte

• Apfel
• Birne
• Banane
• Orange
• Traube

So funktioniert’s:

  1. {% for produkt in produkte %} startet die Schleife
  2. produkt ist die Variable für das aktuelle Element
  3. {{ produkt }} gibt das aktuelle Element aus
  4. {% endfor %} beendet die Schleife

Vergleich zu Python

In Python würdest du schreiben:

1
2
for produkt in produkte:
    print(produkt)

In Jinja ist es fast identisch, nur mit {% %} und {% endfor %} am Ende!

Liste mit Styling

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<!-- templates/produkte.html -->
{% extends "base.html" %}

{% block content %}
<h1>Produktkatalog</h1>

<div class="product-grid">
{% for produkt in produkte %}
    <div class="product-card">
        <h3>{{ produkt }}</h3>
        <p>Frisch und lecker!</p>
        <button>In den Warenkorb</button>
    </div>
{% endfor %}
</div>
{% endblock %}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
/* static/style.css */
.product-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
}

.product-card {
    border: 1px solid #ddd;
    padding: 15px;
    border-radius: 8px;
    text-align: center;
}

Jedes Produkt wird jetzt als schöne Karte angezeigt!

Aufgabe 1: Eigene Liste

  1. Erstelle eine Route /hobbys
  2. Definiere eine Liste mit mindestens 5 Hobbys
  3. Übergebe die Liste ans Template
  4. Zeige alle Hobbys als nummerierte Liste an (<ol>)

Index und Nummerierung

Automatische Nummerierung mit loop.index

Jinja bietet dir die spezielle Variable loop, um auf Informationen über die Schleife zuzugreifen:

1
2
3
4
5
6
7
<h1>Top 5 Produkte</h1>

<ol>
{% for produkt in produkte %}
    <li>Platz {{ loop.index }}: {{ produkt }}</li>
{% endfor %}
</ol>

Ausgabe:

1. Platz 1: Apfel
2. Platz 2: Birne
3. Platz 3: Banane

Nützliche loop-Variablen

VariableBedeutungBeispiel
loop.indexAktuelle Position (ab 1)1, 2, 3…
loop.index0Aktuelle Position (ab 0)0, 1, 2…
loop.firstTrue beim ersten DurchlaufTrue/False
loop.lastTrue beim letzten DurchlaufTrue/False
loop.lengthAnzahl der Elemente5

Praktisches Beispiel:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<ul>
{% for produkt in produkte %}
    <li class="{% if loop.first %}highlight{% endif %}">
        {{ produkt }}
        {% if loop.last %}
            <span> (Letztes Produkt!)</span>
        {% endif %}
    </li>
{% endfor %}
</ul>

<p>Insgesamt {{ loop.length }} Produkte</p>

Warum loop.index und loop.index0?

  • loop.index startet bei 1 → ideal für Anzeige (“Platz 1”, “Platz 2”)
  • loop.index0 startet bei 0 → ideal für Array-Zugriffe oder Berechnungen
  • Wähle je nach Kontext die passende Variable!

enumerate() aus Python nutzen

Du kennst enumerate() aus Python? In Jinja funktioniert es ähnlich:

1
2
3
4
5
# app.py
@app.route("/rangliste")
def rangliste():
    spieler = ["Anna", "Max", "Lisa", "Tom"]
    return render_template("rangliste.html", spieler=spieler)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<!-- templates/rangliste.html -->
<h1>Spieler-Rangliste</h1>

<table>
    <tr>
        <th>Rang</th>
        <th>Name</th>
    </tr>
    {% for index, name in spieler|enumerate %}
    <tr>
        <td>{{ index + 1 }}</td>
        <td>{{ name }}</td>
    </tr>
    {% endfor %}
</table>

Oder einfacher mit loop.index:

1
2
3
4
5
6
{% for name in spieler %}
<tr>
    <td>{{ loop.index }}</td>
    <td>{{ name }}</td>
</tr>
{% endfor %}

Aufgabe 2: Nummerierte Einkaufsliste

Erstelle eine Route /einkaufsliste mit einer Liste von Produkten.

Anforderungen:

  • Zeige jeden Eintrag mit Nummer an
  • Markiere das erste Produkt mit einer CSS-Klasse priority
  • Zeige beim letzten Produkt den Text “(Fertig!)”
  • Zeige unterhalb der Liste die Gesamtanzahl an

Listen mit Dictionaries

In echten Anwendungen speicherst du oft mehrere Informationen pro Eintrag. Dafür nutzt du Dictionaries oder Listen von Dictionaries.

Beispiel: Produktliste mit Preisen

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# app.py
@app.route("/shop")
def shop():
    produkte = [
        {"name": "Apfel", "preis": 0.99},
        {"name": "Birne", "preis": 1.29},
        {"name": "Banane", "preis": 1.49},
        {"name": "Orange", "preis": 1.19}
    ]
    return render_template("shop.html", produkte=produkte)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- templates/shop.html -->
{% extends "base.html" %}

{% block content %}
<h1>Unser Shop</h1>

<table>
    <tr>
        <th>Nr.</th>
        <th>Produkt</th>
        <th>Preis</th>
    </tr>
    {% for produkt in produkte %}
    <tr>
        <td>{{ loop.index }}</td>
        <td>{{ produkt.name }}</td>
        <td>{{ produkt.preis }} €</td>
    </tr>
    {% endfor %}
</table>

<p><strong>Gesamtanzahl Produkte:</strong> {{ produkte|length }}</p>
{% endblock %}

Wichtig:

  • Auf Dictionary-Werte greifst du mit Punkt-Notation zu: produkt.name, produkt.preis
  • Alternative: produkt['name'], produkt['preis'] (wie in Python)

Styling und Interaktivität

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<div class="shop-grid">
{% for produkt in produkte %}
    <div class="product-card {% if loop.index % 2 == 0 %}even{% else %}odd{% endif %}">
        <h3>{{ produkt.name }}</h3>
        <p class="price">{{ produkt.preis }} €</p>

        {% if produkt.preis < 1.20 %}
            <span class="badge sale">Günstig!</span>
        {% endif %}

        <button>Kaufen</button>
    </div>
{% endfor %}
</div>

Das erstellt abwechselnd verschiedene Styles für gerade/ungerade Produkte!

Aufgabe 3: Kontaktliste

Erstelle eine Route /kontakte mit einer Liste von Personen.

Anforderungen:

  • Jede Person hat: Name, E-Mail, Telefon
  • Zeige alle Kontakte in einer Tabelle an
  • Nutze {% if %} um E-Mails mit einem mailto-Link zu versehen

Bonus: Färbe jeden zweiten Eintrag anders (mit loop.index % 2)

Verschachtelte Schleifen

Manchmal hast du Listen in Listen - zum Beispiel Kategorien mit mehreren Produkten.

Beispiel: Kategorisierter Shop

1
2
3
4
5
6
7
8
9
# app.py
@app.route("/kategorien")
def kategorien():
    shop = {
        "Obst": ["Apfel", "Birne", "Banane"],
        "Gemüse": ["Tomate", "Gurke", "Paprika"],
        "Getränke": ["Wasser", "Saft", "Tee"]
    }
    return render_template("kategorien.html", shop=shop)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<!-- templates/kategorien.html -->
{% extends "base.html" %}

{% block content %}
<h1>Shop nach Kategorien</h1>

{% for kategorie, produkte in shop.items() %}
    <div class="kategorie">
        <h2>{{ kategorie }}</h2>
        <ul>
        {% for produkt in produkte %}
            <li>{{ produkt }}</li>
        {% endfor %}
        </ul>
    </div>
{% endfor %}
{% endblock %}

Ausgabe:

Obst
• Apfel
• Birne
• Banane

Gemüse
• Tomate
• Gurke
• Paprika

Getränke
• Wasser
• Saft
• Tee

Komplexeres Beispiel mit Nummerierung

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{% for kategorie, produkte in shop.items() %}
    <div class="kategorie">
        <h2>{{ loop.index }}. {{ kategorie }} ({{ produkte|length }} Produkte)</h2>
        <ol>
        {% for produkt in produkte %}
            <li>{{ produkt }}</li>
        {% endfor %}
        </ol>
    </div>
{% endfor %}

Äußere und innere Schleife

  • Die äußere Schleife durchläuft die Kategorien
  • Die innere Schleife durchläuft die Produkte pro Kategorie
  • Jede Schleife hat ihre eigene loop-Variable
  • Wenn du in der inneren Schleife auf die äußere zugreifen musst, speichere Werte in Variablen

Jinja Filter

Filter verändern die Ausgabe von Variablen. Du wendest sie mit dem Pipe-Symbol | an.

Die wichtigsten Filter

1. length - Anzahl der Elemente

1
2
3
4
5
6
7
<p>Wir haben {{ produkte|length }} Produkte.</p>

{% if produkte|length > 10 %}
    <p>Großes Sortiment!</p>
{% else %}
    <p>Kleines Sortiment.</p>
{% endif %}

2. upper / lower - Groß-/Kleinschreibung

1
2
3
4
5
{% for produkt in produkte %}
    <li>{{ produkt|upper }}</li>  <!-- APFEL, BIRNE -->
{% endfor %}

<p>Kategorie: {{ kategorie|lower }}</p>  <!-- obst, gemüse -->

3. capitalize / title - Erste Buchstaben groß

1
2
<p>{{ "hallo welt"|capitalize }}</p>  <!-- Hallo welt -->
<p>{{ "hallo welt"|title }}</p>       <!-- Hallo Welt -->

4. default - Standardwert bei leerem Wert

1
<p>Name: {{ name|default("Unbekannt") }}</p>

5. join - Liste zu String verbinden

1
2
<p>Produkte: {{ produkte|join(", ") }}</p>
<!-- Ausgabe: Apfel, Birne, Banane -->

6. sort - Liste sortieren

1
2
3
{% for produkt in produkte|sort %}
    <li>{{ produkt }}</li>
{% endfor %}

Filter kombinieren

Du kannst mehrere Filter hintereinander anwenden:

1
2
3
4
5
<p>{{ produkte|length|default(0) }}</p>

{% for produkt in produkte|sort|reverse %}
    <li>{{ produkt|upper }}</li>
{% endfor %}

Praktisches Beispiel

1
2
3
4
5
6
7
8
9
<h1>Produktübersicht ({{ produkte|length }} Artikel)</h1>

{% for produkt in produkte|sort %}
    <div class="product">
        <h3>{{ produkt.name|title }}</h3>
        <p>{{ produkt.beschreibung|default("Keine Beschreibung verfügbar") }}</p>
        <p class="price">{{ produkt.preis }} €</p>
    </div>
{% endfor %}

Filter vs Python-Funktionen

Filter in Jinja ähneln Python-Funktionen:

  • produkte|lengthlen(produkte)
  • text|uppertext.upper()
  • liste|sortsorted(liste)

Filter sind aber Template-spezifisch und einfacher zu schreiben!

Leere Listen behandeln

Was passiert, wenn die Liste leer ist? Jinja bietet {% else %} für Schleifen:

1
2
3
4
5
6
7
8
9
<h1>Meine To-Dos</h1>

<ul>
{% for todo in todos %}
    <li>{{ todo }}</li>
{% else %}
    <li>Keine To-Dos vorhanden! Zeit zum Entspannen. 🎉</li>
{% endfor %}
</ul>

Wie es funktioniert:

  • Wenn todos Elemente hat → normale Schleife
  • Wenn todos leer ist → Inhalt von {% else %} wird angezeigt

Alternativ mit if:

1
2
3
4
5
6
7
8
9
{% if produkte|length > 0 %}
    <ul>
    {% for produkt in produkte %}
        <li>{{ produkt }}</li>
    {% endfor %}
    </ul>
{% else %}
    <p>Keine Produkte verfügbar.</p>
{% endif %}

Übungen

Übung 1: Einfache Produktliste

Erstelle eine Route /shop-basic mit einer Liste von mindestens 6 Produkten.

Anforderungen:

  1. Zeige alle Produkte in einer nummerierten Liste an
  2. Nutze loop.index für die Nummerierung
  3. Das erste Produkt soll die CSS-Klasse featured bekommen
  4. Zeige unterhalb: “Insgesamt X Produkte”

Tipp: Nutze {% if loop.first %} für das erste Element!

Lösung zeigen
1
2
3
4
5
# app.py
@app.route("/shop-basic")
def shop_basic():
    produkte = ["Laptop", "Maus", "Tastatur", "Monitor", "Webcam", "Headset"]
    return render_template("shop_basic.html", produkte=produkte)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<!-- templates/shop_basic.html -->
{% extends "base.html" %}

{% block title %}Shop{% endblock %}

{% block content %}
<h1>Unser Sortiment</h1>

<ol>
{% for produkt in produkte %}
    <li class="{% if loop.first %}featured{% endif %}">
        {{ loop.index }}. {{ produkt }}
    </li>
{% endfor %}
</ol>

<p><strong>Insgesamt {{ produkte|length }} Produkte</strong></p>
{% endblock %}
1
2
3
4
5
6
/* static/style.css */
.featured {
    color: gold;
    font-weight: bold;
    font-size: 1.2em;
}

Übung 2: Shop mit Preisen (Tabelle)

Erstelle eine Route /shop-tabelle mit Produkten und Preisen.

Anforderungen:

  1. Jedes Produkt hat Name und Preis (als Dictionary)
  2. Zeige alle in einer HTML-Tabelle an
  3. Produkte unter 10€ sollen grün markiert werden
  4. Berechne und zeige den Gesamtpreis aller Produkte

Hinweis: Für den Gesamtpreis musst du in Python die Summe berechnen und ans Template übergeben.

Lösung zeigen
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# app.py
@app.route("/shop-tabelle")
def shop_tabelle():
    produkte = [
        {"name": "Maus", "preis": 15.99},
        {"name": "Tastatur", "preis": 29.99},
        {"name": "USB-Kabel", "preis": 5.99},
        {"name": "Monitor", "preis": 199.99},
        {"name": "Webcam", "preis": 8.99}
    ]

    # Gesamtpreis berechnen
    gesamtpreis = sum(p["preis"] for p in produkte)

    return render_template("shop_tabelle.html", produkte=produkte, gesamtpreis=gesamtpreis)
 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
<!-- templates/shop_tabelle.html -->
{% extends "base.html" %}

{% block content %}
<h1>Produktübersicht</h1>

<table>
    <thead>
        <tr>
            <th>Nr.</th>
            <th>Produkt</th>
            <th>Preis</th>
        </tr>
    </thead>
    <tbody>
    {% for produkt in produkte %}
        <tr class="{% if produkt.preis < 10 %}guenstig{% endif %}">
            <td>{{ loop.index }}</td>
            <td>{{ produkt.name }}</td>
            <td>{{ produkt.preis }} €</td>
        </tr>
    {% endfor %}
    </tbody>
    <tfoot>
        <tr>
            <td colspan="2"><strong>Gesamtpreis:</strong></td>
            <td><strong>{{ gesamtpreis }} €</strong></td>
        </tr>
    </tfoot>
</table>

<p>{{ produkte|length }} Produkte im Sortiment</p>
{% endblock %}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
/* static/style.css */
table {
    width: 100%;
    border-collapse: collapse;
}

th, td {
    padding: 10px;
    text-align: left;
    border-bottom: 1px solid #ddd;
}

.guenstig {
    background-color: lightgreen;
}

tfoot {
    font-weight: bold;
    background-color: #f0f0f0;
}

Übung 3: Verschachtelte Kategorien

Erstelle eine Route /shop-kategorien mit Kategorien und Produkten.

Anforderungen:

  1. Mindestens 3 Kategorien mit je 3-5 Produkten
  2. Zeige Kategorien als Überschriften
  3. Bei jeder Kategorie: Anzahl der Produkte anzeigen
  4. Produkte als Liste unter jeder Kategorie
  5. Nutze Filter um Kategorienamen groß zu schreiben

Bonus: Zähle und zeige die Gesamtanzahl aller Produkte (über alle Kategorien).

Lösung zeigen
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# app.py
@app.route("/shop-kategorien")
def shop_kategorien():
    kategorien = {
        "Elektronik": ["Laptop", "Smartphone", "Tablet", "Kopfhörer"],
        "Büro": ["Stift", "Papier", "Ordner"],
        "Gaming": ["Maus", "Tastatur", "Headset", "Controller", "Bildschirm"]
    }

    # Gesamtanzahl berechnen
    gesamt = sum(len(produkte) for produkte in kategorien.values())

    return render_template("shop_kategorien.html", kategorien=kategorien, gesamt=gesamt)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- templates/shop_kategorien.html -->
{% extends "base.html" %}

{% block content %}
<h1>Shop-Kategorien</h1>

{% for kategorie, produkte in kategorien.items() %}
    <div class="kategorie-block">
        <h2>{{ kategorie|upper }} ({{ produkte|length }} Artikel)</h2>

        <ul>
        {% for produkt in produkte %}
            <li>{{ produkt }}</li>
        {% endfor %}
        </ul>
    </div>
{% endfor %}

<hr>
<p><strong>Gesamtanzahl aller Produkte: {{ gesamt }}</strong></p>
{% endblock %}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/* static/style.css */
.kategorie-block {
    margin-bottom: 30px;
    padding: 15px;
    border-left: 4px solid #007bff;
    background-color: #f8f9fa;
}

.kategorie-block h2 {
    color: #007bff;
    margin-top: 0;
}

Zusammenfassung

Das Wichtigste auf einen Blick:

For-Schleifen:

  • {% for item in liste %}...{% endfor %}
  • Durchläuft Listen, erzeugt HTML für jedes Element
  • {% else %} für leere Listen

loop-Variable:

  • loop.index - Position ab 1
  • loop.index0 - Position ab 0
  • loop.first - True beim ersten Element
  • loop.last - True beim letzten Element
  • loop.length - Anzahl der Elemente

Dictionaries in Schleifen:

  • for item in liste_von_dicts
  • Zugriff: item.name oder item['name']
  • Dictionary durchlaufen: for key, value in dict.items()

Verschachtelte Schleifen:

  • Äußere Schleife für Kategorien/Gruppen
  • Innere Schleife für Elemente pro Gruppe
  • Jede Schleife hat eigene loop-Variable

Filter:

  • |length - Anzahl der Elemente
  • |upper / |lower - Groß-/Kleinschreibung
  • |title / |capitalize - Erste Buchstaben groß
  • |sort / |reverse - Sortieren/Umkehren
  • |join(", ") - Liste zu String
  • |default("Wert") - Standardwert bei leer
  • Filter kombinierbar: liste|sort|reverse

Häufige Muster:

 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
<!-- Einfache Liste -->
{% for item in items %}
    <li>{{ item }}</li>
{% endfor %}

<!-- Mit Nummerierung -->
{% for item in items %}
    <li>{{ loop.index }}. {{ item }}</li>
{% endfor %}

<!-- Mit Bedingung -->
{% for item in items %}
    <li class="{% if loop.first %}featured{% endif %}">
        {{ item }}
    </li>
{% endfor %}

<!-- Dictionary-Liste -->
{% for item in items %}
    <div>{{ item.name }} - {{ item.preis }}€</div>
{% endfor %}

<!-- Verschachtelt -->
{% for kategorie, produkte in shop.items() %}
    <h2>{{ kategorie }}</h2>
    {% for produkt in produkte %}
        <li>{{ produkt }}</li>
    {% endfor %}
{% endfor %}

Nächste Schritte

Jetzt kennst du Schleifen in Jinja! Im nächsten Modul lernst du:

Zuletzt aktualisiert am