mitmario.dev

Werte aus der Adresse

Python Sandbox 4 Min Lesezeit 4 BeispieleLektion 4 von 7

Das Haushaltsbuch hat dreizehn Ausgaben, und jede soll ihre eigene Seite bekommen. Dafür dreizehn Routen zu schreiben, /ausgabe/1 bis /ausgabe/13, wäre derselbe Unsinn wie dreizehn Variablen statt einer Liste. Eine Route kann stattdessen einen Teil ihrer Adresse offenlassen und ihn als Wert an die Funktion geben.

Ein variabler Teil im Pfad

Eine Nummer in der Adresse
from flask import Flask, render_template

from haushalt import AUSGABEN, euro

app = Flask(__name__)
app.add_template_filter(euro)


@app.get("/ausgabe/<int:nummer>")
def ausgabe(nummer):
    eintrag = AUSGABEN[nummer - 1]
    return render_template("ausgabe.html", nummer=nummer, anzahl=len(AUSGABEN), ausgabe=eintrag)

<int:nummer> im Pfad ist ein variabler Teil (variable section). Er passt auf jede ganze Zahl an dieser Stelle, und Flask ruft die Funktion mit dieser Zahl als Parameter nummer auf. /ausgabe/3 ruft also ausgabe(3) auf, und auf der Seite steht die dritte Ausgabe, die Bäckerei. Über den Link unten kommst du durch die ganze Liste.

Das int: davor sagt, welcher Typ an dieser Stelle stehen darf, und macht den Wert gleich zu einer Zahl, wie int() in Lektion 2.5. Ohne die Angabe käme "3" als Text an. Wenn etwas nicht passt, etwa /ausgabe/drei, gilt die Route gar nicht, und es kommt eine 404.

Ein variabler Teil ohne Angabe, also <name>, nimmt jeden Text bis zum nächsten /, auch einen mit Umlaut. /kategorie/Mobilität schreibt der Browser beim Fragen um, so wie requests es auch tut, und Flask macht wieder Mobilität daraus.

Zwei Adressen gehen in diesem Beispiel schief. /ausgabe/14 gibt es nicht, und AUSGABEN[13] endet mit IndexError: list index out of range. Das ist ein Absturz mit Status 500, und in der Adresszeile lässt sich das leicht nachprüfen. /ausgabe/0 zeigt dagegen ohne jede Meldung die letzte Ausgabe, weil AUSGABEN[-1] in Python die letzte ist, wie bei jeder Liste. Beides passiert, weil niemand nachsieht, ob es zu der Nummer aus der Adresse überhaupt eine Ausgabe gibt.

Eine ehrliche 404

Eine ehrliche 404
from flask import Flask, abort, render_template

from haushalt import AUSGABEN, euro

app = Flask(__name__)
app.add_template_filter(euro)


@app.get("/ausgabe/<int:nummer>")
def ausgabe(nummer):
    if nummer < 1 or nummer > len(AUSGABEN):
        abort(404)
    eintrag = AUSGABEN[nummer - 1]
    return render_template("ausgabe.html", nummer=nummer, anzahl=len(AUSGABEN), ausgabe=eintrag)

Wenn es zu einer Adresse nichts gibt, gehört dazu eine 404, „gibt es nicht“. Dafür ist abort(404) da. Damit endet die Anfrage an genau dieser Stelle, und Flask schickt seine Seite Not Found mit dem Statuscode 404, so wie bei einem Pfad ohne Route. Die Zeilen danach laufen nicht mehr.

Das if steht deshalb vor dem Zugriff auf die Liste. Für den, der fragt, ist der Unterschied deutlich. Eine 404 heißt, er hat sich in der Adresse vertan. Eine 500 heißt, der Server ist kaputt. Ein Programm wie in Abschnitt 17, das deine Seite fragt, kann daran entscheiden, was es tut.

Werte hinter dem Fragezeichen

Werte hinter dem Fragezeichen
from flask import Flask, render_template, request

from haushalt import AUSGABEN, euro

app = Flask(__name__)
app.add_template_filter(euro)


@app.get("/suche")
def suche():
    wort = request.args.get("wort", "")
    treffer = [ausgabe for ausgabe in AUSGABEN if wort.lower() in ausgabe["empfaenger"].lower()]
    return render_template("suche.html", wort=wort, treffer=treffer)

Nicht jeder Wert gehört in den Pfad. Ein Suchwort ist kein Teil der Adresse wie eine Nummer. Es ist eine Angabe zur Frage, und solche Angaben stehen wie in Abschnitt 17 hinter einem Fragezeichen, etwa /suche?wort=krume.

Flask legt sie in request.args ab, einem Dictionary für die Parameter der Anfrage. request kommt aus flask und steht in jeder Route für die Anfrage, die gerade läuft.

request.args.get("wort", "") liest den Wert mit einem Standardwert, wie get an einem Dictionary in Lektion 7.1. Ohne Fragezeichen gibt es dann ein leeres Suchwort und alle dreizehn Ausgaben. request.args["wort"] ginge auch, solange der Parameter dasteht. Fehlt er, kommt 400 Bad Request zurück, und warum, steht nirgends. Deshalb ist get hier fast immer die bessere Wahl.

Das Formular auf der Seite macht übrigens nichts anderes. Wenn du im Browser ein anderes Wort eintippst und abschickst, siehst du es in der Adresszeile wieder. Der Browser hängt das Feld nämlich als ?wort=… an und fragt diese Adresse.

Ein anderer Name im Pfad

Ein anderer Name im Pfad
from flask import Flask, abort, render_template

from haushalt import AUSGABEN, euro

app = Flask(__name__)
app.add_template_filter(euro)


@app.get("/ausgabe/<int:nummer>")
def ausgabe(n):
    if n < 1 or n > len(AUSGABEN):
        abort(404)
    return render_template("ausgabe.html", nummer=n, anzahl=len(AUSGABEN), ausgabe=AUSGABEN[n - 1])

Hier heißt der variable Teil nummer, der Parameter der Funktion aber n. Flask gibt den Wert unter dem Namen aus dem Pfad weiter, also als ausgabe(nummer=3), und einen Parameter mit diesem Namen hat die Funktion nicht. Im Browser steht ein Traceback, der so endet:

TypeError: ausgabe() got an unexpected keyword argument 'nummer'

Übersetzt heißt das, ausgabe() hat ein benanntes Argument nummer bekommen, mit dem sie nichts anfangen kann. Das ist dieselbe Meldung wie bei jedem Aufruf mit einem falschen Namen. Der Name im Pfad und der des Parameters müssen gleich sein.

Was die Aufgabe will

Jede Kategorie bekommt ihre eigene Seite mit den Ausgaben darin, /kategorie/Lebensmittel und so weiter. Die Vorlage dafür ist schon fertig. Für eine Kategorie, die es nicht gibt, soll eine ehrliche 404 kommen. Eine Kategorie, in der noch nichts steht, gibt es trotzdem.

Zum Mitnehmen

Bei @app.get("/ausgabe/<int:nummer>") ist der Teil in spitzen Klammern variabel, die Funktion bekommt den Wert aus der Adresse als nummer, und wegen int: kommen nur ganze Zahlen durch. Werte hinter dem Fragezeichen liegen in request.args und lassen sich mit request.args.get("wort", "") samt Standardwert lesen. Gibt es zu einem Wert nichts, endet die Anfrage mit abort(404) und einer ehrlichen 404.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.

Was in dieser Lektion steckt

  • Artikel mit 4 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe, dein Code läuft auf einem Server

    Öffnet sich mit dem Basis Konto.