# Hello, Flask

Quelle: https://dev.inf.zone/exercises/13/hello-flask/

## Aufgabe

**Auf einen Blick**

- **Was:** Sie bauen Schritt für Schritt Ihre erste Web-App mit [Flask](https://flask.palletsprojects.com): erst eine Seite mit „Hello, World!“, am Ende ein Formular, das nach Ihrer Lieblingsfarbe fragt und die Antwort anzeigt.
- **Ordner:** `helloflask` (die ZIP enthält nur den leeren Ordner; alle Dateien legen Sie selbst an).
- **Dateien:** `app.py`, dazu im Unterordner `templates` die Vorlagen `layout.html`, `index.html` und `color.html`.
- **Ergebnis:** `/` zeigt ein Formular mit einer Auswahlliste; nach dem Absenden erscheint „Ihre Lieblingsfarbe ist red.“ (bzw. `blue`) in dieser Farbe.
- **Prüfen:** im Browser und mit `check50`.

**Wozu das Ganze?**

Flask ist ein kleines Framework für Web-Apps in Python; Jinja ist die Template-Sprache, mit der Flask HTML-Seiten erzeugt. Diese Aufgabe beginnt mit dem Minimum, einer einzigen Python-Datei. Auf den Bausteinen – Routen, Templates, Formulare, GET und POST – bauen die Aufgaben [Geburtstage](https://dev.inf.zone/exercises/13/birthdays/) und [Finanzen](https://dev.inf.zone/exercises/13/finance/) auf.

### So funktioniert eine Flask-App

1. **Route:** `@app.route("/")` verbindet einen URL-Pfad mit einer Python-Funktion. Fragt der Browser `/` an, ruft Flask diese Funktion auf.
2. **Antwort:** Was die Funktion zurückgibt, schickt Flask an den Browser – einen Text oder eine ganze HTML-Seite.
3. **Templates:** HTML-Dateien liegen im Ordner `templates`. `render_template("index.html", color=color)` füllt eine Vorlage aus und gibt das fertige HTML zurück; `{{ color }}` in der Vorlage wird durch den übergebenen Wert ersetzt.
4. **Layout:** Eine Vorlage kann mit `{% extends "layout.html" %}` eine andere erweitern: Sie übernimmt deren HTML und füllt nur die Blöcke (`{% block body %}…{% endblock %}`).
5. **Formular:** Beim Absenden schickt der Browser für jedes Feld ein Paar aus `name` und Wert an die Adresse aus `action`, mit der Methode aus `method`. In Flask liest `request.form.get("color")` den Wert des Feldes `color`.
6. **Methoden:** Eine Route nimmt zunächst nur GET-Anfragen an. POST muss man mit `methods=["GET", "POST"]` erlauben, sonst antwortet Flask mit `405 Method Not Allowed`. Welche Methode gerade ankam, steht in `request.method`.

**Beispiel** – die fertige App aus Schritt 4:

| Anfrage des Browsers        | Was in `app.py` passiert                  | Antwort                                                        |
| --------------------------- | ----------------------------------------- | -------------------------------------------------------------- |
| GET `/`                     | `index()`, Zweig für GET                  | `index.html` im Layout: Überschrift „Hallo, Flask“ und Formular |
| POST `/` mit `color=blue`   | `index()`, Zweig für POST                 | `color.html`: „Ihre Lieblingsfarbe ist blue.“ in Blau          |
| POST `/`, aber Route ohne `methods=…` | –                               | `405 Method Not Allowed`                                       |

## Aufgabenmaterial

**Aufgabenmaterial herunterladen**

Öffnen Sie VS Code entsprechend Ihrem [Setup](https://dev.inf.zone/extras/setup/), klicken Sie auf Ihr Terminalfenster und führen Sie `cd` aus. Die Eingabeaufforderung Ihres Terminalfensters sollte ungefähr wie folgt aussehen:

```bash
$
```

Führen Sie den Befehl
```bash
wget https://dev.inf.zone/download/exercises/13/helloflask.zip
```
aus, um in Ihrem Codespace die ZIP `helloflask.zip` herunterzuladen.

Nun können Sie
```bash
unzip helloflask.zip
```
ausführen, um die ZIP in den Ordner `helloflask` zu entpacken.

Sie benötigen die ZIP-Datei nicht mehr, daher können Sie den Befehl
```bash
rm helloflask.zip
```
ausführen und bei der Aufforderung mit „y“ gefolgt von der Eingabetaste antworten, um die heruntergeladene ZIP-Datei zu entfernen.

Geben Sie nun den Befehl
```bash
cd helloflask
```
ein und drücken Sie anschließend die Eingabetaste, um in dieses Verzeichnis zu wechseln (d. h., es zu öffnen). Ihre Eingabeaufforderung sollte nun wie folgt aussehen:
```bash
helloflask/ $
```

## Spezifikation

Arbeiten Sie die vier Schritte der Reihe nach ab und starten Sie die App nach jedem Schritt im Ordner `helloflask` mit

```bash
flask run
```

Flask gibt einen Link aus, den Sie anklicken können – ähnlich wie bei `http-server`. Nach jeder Änderung beenden Sie den Server mit Strg+C und starten ihn neu.

### Schritt 1: Hello, World!

Legen Sie `app.py` mit diesem Inhalt an:

```python
from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return "Hello, World!"
```

- `app = Flask(__name__)` erzeugt die App; `__name__` ist der Name der aktuellen Python-Datei, damit Flask später Ordner wie `templates` findet.
- `@app.route("/")` ist ein Dekorator (Regel 1): Er meldet die Funktion darunter für den Pfad `/` an. Hieße die Funktion `homepage` statt `index`, würde Flask eben `homepage` aufrufen.
- `index` gibt den Text zurück, den der Browser anzeigt (Regel 2).

Geben Sie danach statt des Textes `<h1>Hello, World!</h1>` zurück. Was ändert sich im Browser?

### Schritt 2: Templates

HTML gehört nicht in den Python-Code, sondern in eigene Dateien. Legen Sie mit `mkdir templates` den Ordner `templates` an; alle HTML-Dateien dieser Aufgabe kommen dort hinein.

`templates/layout.html` enthält das Gerüst, das alle Seiten gemeinsam haben, und einen Block `body`, den jede Seite selbst füllt (Regel 4). Ein Layout kann beliebig viele Blöcke mit verschiedenen Namen haben.

```jinja
<!DOCTYPE html>

<html lang="de">
    <head>
        <title>Hallo, Flask</title>
    </head>
    <body>
        <h1>Hallo, Flask</h1>
        {% block body %}{% endblock %}
    </body>
</html>
```

`templates/index.html` erweitert das Layout:

```jinja
{% extends "layout.html" %}

{% block body %}
    <p>Als Nächstes fügen wir hier ein Formular ein und erhalten eine POST-Aktion!</p>
{% endblock %}
```

Ändern Sie `app.py` so, dass `index` die Vorlage rendert, statt Text zurückzugeben. `render_template` setzt `index.html` und `layout.html` zu einer Seite zusammen; `request` brauchen Sie erst in Schritt 4.

```python
from flask import Flask, render_template, request

app = Flask(__name__)

@app.route("/")
def index():
    return render_template("index.html")
```

### Schritt 3: Formular

Ersetzen Sie den Inhalt von `templates/index.html` durch ein Formular:

```jinja
{% extends "layout.html" %}

{% block body %}
    <form action="/" method="post">
        Was ist Ihre Lieblingsfarbe?
        <select name="color">
            <option value="red">Harvard Crimson</option>
            <option value="blue">Yale Blue</option>
        </select>
        <button type="submit">Absenden</button>
    </form>
{% endblock %}
```

- Die Auswahlliste heißt `color` (Attribut `name`); unter diesem Namen liest `app.py` sie später aus.
- Gesendet wird der `value` der gewählten Option, also `red` oder `blue` – nicht der angezeigte Text „Harvard Crimson“.

Senden Sie das Formular ab. Flask antwortet mit „Method Not Allowed“ (Regel 6): Die Route `/` nimmt noch keine POST-Anfragen an.

### Schritt 4: POST

Erlauben Sie POST und unterscheiden Sie in `app.py` die beiden Methoden:

```python
from flask import Flask, render_template, request

app = Flask(__name__)

@app.route("/", methods=["GET", "POST"])
def index():
    if request.method == "GET":
        return render_template("index.html")
    else:
        print("Formular wurde abgesendet!")
        color = request.form.get("color")
        return render_template("color.html", color=color)
```

- **GET:** wie bisher das Formular.
- **POST:** `print` schreibt „Formular wurde abgesendet!“ ins Terminal, in dem `flask run` läuft – eine Hilfe beim Debuggen, die Sie später wieder entfernen. Dann liest `request.form.get("color")` den gesendeten Wert und übergibt ihn unter demselben Namen an `color.html`.

Legen Sie `templates/color.html` an:

```jinja
{% extends "layout.html" %}

{% block body %}
    <p style="color: {{ color }}">Ihre Lieblingsfarbe ist {{ color }}.</p>
{% endblock %}
```

`{{ color }}` darf überall im HTML stehen: hier einmal im Satz und einmal als Wert der CSS-Eigenschaft `color`.

**Zum Schluss:** Bisher verlässt sich `app.py` darauf, dass nur `red` oder `blue` ankommt. Aber jeder kann eine POST-Anfrage mit beliebigem Wert schicken, auch ohne Ihr Formular. Ergänzen Sie eine Prüfung, die nur gültige Farben durchlässt.

## Hilfestellung

Klicken Sie auf die folgenden Tipps, um einige Ratschläge zu erhalten. Versuchen Sie aber zunächst, selbst so weit wie möglich zu kommen.

**Fehlermeldung „TemplateNotFound“**

- Liegen `layout.html`, `index.html` und `color.html` im Ordner `templates` und dieser direkt neben `app.py`?
- Stimmt die Schreibweise des Dateinamens in `render_template(…)` genau?

**Nur gültige Farben durchlassen**

- Legen Sie die erlaubten Werte in einer Liste ab, etwa `["red", "blue"]`, und prüfen Sie mit `in`, ob `color` darin vorkommt.
- Ist der Wert ungültig, geben Sie eine kurze Fehlermeldung zurück oder zeigen wieder das Formular an.

## Testen

Prüfen Sie im Browser:

- **Schritt 1:** `/` zeigt „Hello, World!“.
- **Schritt 2:** `/` zeigt die Überschrift „Hallo, Flask“ und darunter den Absatz aus `index.html`.
- **Schritt 3:** `/` zeigt das Formular; Absenden führt zu „Method Not Allowed“.
- **Schritt 4:** Nach dem Absenden erscheint „Ihre Lieblingsfarbe ist red.“ in Rot bzw. „… blue.“ in Blau, und im Terminal steht „Formular wurde abgesendet!“.

### Korrektheit

Führen Sie in Ihrem Terminal den folgenden Befehl aus, um die Korrektheit Ihrer Arbeit zu überprüfen:

```bash
check50 -l inf-zone/exercises/2026/helloflask
```

## Abgeben

Geben Sie im Ordner `helloflask` ab:

```bash
inf upload helloflask
```

Danach sehen Sie, welche Tests Ihr Programm besteht und ob die Übung für die Bonuspunkte zählt. Wie Sie `inf` installieren und sich anmelden, steht unter [Abgeben mit `inf`](https://dev.inf.zone/faq/uebung-solutions/#inf-installieren).

## Zum Weiterlesen: Flask, Jinja und Dekoratoren

Für die Lösung der Aufgabe nicht nötig.

![flask-logo](https://dev.inf.zone/exercises/13/hello-flask/./flask-logo.png)

[Flask](https://flask.palletsprojects.com) ist ein [Microframework](https://en.wikipedia.org/wiki/Microframework): Es bringt nur das Nötigste mit (Routen, Anfragen, Antworten, Templates), alles Weitere ergänzt man bei Bedarf. Die Template-Sprache [Jinja](https://palletsprojects.com/projects/jinja/) stammt vom selben Projekt.

`Flask` ist eine Klasse (siehe Short zum [Objektorientierten Programmieren](https://dev.inf.zone/lectures/6-python/short-6-4-objektorientiertes-programmieren/)), gewissermaßen eine Vorlage für Web-Apps; `app = Flask(__name__)` erzeugt daraus eine konkrete App.

Ein Dekorator wie `@app.route("/")` nimmt die Funktion darunter und erweitert sie um eigene Funktionalität – hier: Flask merkt sich, bei welchem Pfad die Funktion aufgerufen werden soll. Mehr dazu in dieser [Einführung in Dekoratoren](https://realpython.com/primer-on-python-decorators/) und im Short zum [Objektorientierten Programmieren](https://dev.inf.zone/lectures/6-python/short-6-4-objektorientiertes-programmieren/) (auch `@property` ist ein Dekorator).

Mehr über HTML-Formulare finden Sie bei [W3Schools](https://www.w3schools.com/html/html_forms.asp).
