Zum Inhalt

Query-Parameter und Stringvalidierung

FastAPI erlaubt es Ihnen, Ihre Parameter zusätzlich zu validieren, und zusätzliche Informationen hinzuzufügen.

Nehmen wir als Beispiel die folgende Anwendung:

from fastapi import FastAPI

app = FastAPI()


@app.get("/items/")
async def read_items(q: str | None = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Union

from fastapi import FastAPI

app = FastAPI()


@app.get("/items/")
async def read_items(q: Union[str, None] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Der Query-Parameter q hat den Typ Union[str, None] (oder str | None in Python 3.10), was bedeutet, er ist entweder ein str oder None. Der Defaultwert ist None, also weiß FastAPI, der Parameter ist nicht erforderlich.

Hinweis

FastAPI weiß nur dank des definierten Defaultwertes =None, dass der Wert von q nicht erforderlich ist

Union[str, None] hingegen erlaubt ihren Editor, Sie besser zu unterstützen und Fehler zu erkennen.

Zusätzliche Validierung

Wir werden bewirken, dass, obwohl q optional ist, wenn es gegeben ist, seine Länge 50 Zeichen nicht überschreitet.

Query und Annotated importieren

Importieren Sie zuerst:

  • Query von fastapi
  • Annotated von typing (oder von typing_extensions in Python unter 3.9)

In Python 3.9 oder darüber, ist Annotated Teil der Standardbibliothek, also können Sie es von typing importieren.

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(max_length=50)] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

In Versionen unter Python 3.9 importieren Sie Annotated von typing_extensions.

Es wird bereits mit FastAPI installiert sein.

from typing import Union

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(max_length=50)] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Info

FastAPI unterstützt (und empfiehlt die Verwendung von) Annotated seit Version 0.95.0.

Wenn Sie eine ältere Version haben, werden Sie Fehler angezeigt bekommen, wenn Sie versuchen, Annotated zu verwenden.

Bitte aktualisieren Sie FastAPI daher mindestens zu Version 0.95.1, bevor Sie Annotated verwenden.

Annotated im Typ des q-Parameters verwenden

Erinnern Sie sich, wie ich in Einführung in Python-Typen sagte, dass Sie mittels Annotated Metadaten zu Ihren Parametern hinzufügen können?

Jetzt ist es an der Zeit, das mit FastAPI auszuprobieren. 🚀

Wir hatten diese Typannotation:

q: str | None = None
q: Union[str, None] = None

Wir wrappen das nun in Annotated, sodass daraus wird:

q: Annotated[str | None] = None
q: Annotated[Union[str, None]] = None

Beide Versionen bedeuten dasselbe: q ist ein Parameter, der str oder None sein kann. Standardmäßig ist er None.

Wenden wir uns jetzt den spannenden Dingen zu. 🎉

Query zu Annotated im q-Parameter hinzufügen

Jetzt, da wir Annotated für unsere Metadaten deklariert haben, fügen Sie Query hinzu, und setzen Sie den Parameter max_length auf 50:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(max_length=50)] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Union

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(max_length=50)] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Beachten Sie, dass der Defaultwert immer noch None ist, sodass der Parameter immer noch optional ist.

Aber jetzt, mit Query(max_length=50) innerhalb von Annotated, sagen wir FastAPI, dass es diesen Wert aus den Query-Parametern extrahieren soll (das hätte es sowieso gemacht 🤷) und dass wir eine zusätzliche Validierung für diesen Wert haben wollen (darum machen wir das, um die zusätzliche Validierung zu bekommen). 😎

FastAPI wird nun:

  • Die Daten validieren und sicherstellen, dass sie nicht länger als 50 Zeichen sind
  • Dem Client einen verständlichen Fehler anzeigen, wenn die Daten ungültig sind
  • Den Parameter in der OpenAPI-Schema-Pfadoperation dokumentieren (sodass er in der automatischen Dokumentation angezeigt wird)

Alternativ (alt): Query als Defaultwert

Frühere Versionen von FastAPI (vor 0.95.0) benötigten Query als Defaultwert des Parameters, statt es innerhalb von Annotated unterzubringen. Die Chance ist groß, dass Sie Quellcode sehen, der das immer noch so macht, darum erkläre ich es Ihnen.

Tipp

Verwenden Sie für neuen Code, und wann immer möglich, Annotated, wie oben erklärt. Es gibt mehrere Vorteile (unten erläutert) und keine Nachteile. 🍰

So würden Sie Query() als Defaultwert Ihres Funktionsparameters verwenden, den Parameter max_length auf 50 gesetzt:

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: str | None = Query(default=None, max_length=50)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Union[str, None] = Query(default=None, max_length=50)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Da wir in diesem Fall (ohne die Verwendung von Annotated) den Parameter-Defaultwert None mit Query() ersetzen, müssen wir nun dessen Defaultwert mit dem Parameter Query(default=None) deklarieren. Das dient demselben Zweck, None als Defaultwert für den Funktionsparameter zu setzen (zumindest für FastAPI).

Sprich:

q: Union[str, None] = Query(default=None)

... macht den Parameter optional, mit dem Defaultwert None, genauso wie:

q: Union[str, None] = None

Und in Python 3.10 und darüber macht:

q: str | None = Query(default=None)

... den Parameter optional, mit dem Defaultwert None, genauso wie:

q: str | None = None

Nur, dass die Query-Versionen den Parameter explizit als Query-Parameter deklarieren.

Info

Bedenken Sie, dass:

= None

oder:

= Query(default=None)

der wichtigste Teil ist, um einen Parameter optional zu machen, da dieses None der Defaultwert ist, und das ist es, was diesen Parameter nicht erforderlich macht.

Der Teil mit Union[str, None] erlaubt es Ihrem Editor, Sie besser zu unterstützen, aber er sagt FastAPI nicht, dass dieser Parameter optional ist.

Jetzt können wir Query weitere Parameter übergeben. Fangen wir mit dem max_length Parameter an, der auf Strings angewendet wird:

q: Union[str, None] = Query(default=None, max_length=50)

Das wird die Daten validieren, einen verständlichen Fehler ausgeben, wenn die Daten nicht gültig sind, und den Parameter in der OpenAPI-Schema-Pfadoperation dokumentieren.

Query als Defaultwert oder in Annotated

Bedenken Sie, dass wenn Sie Query innerhalb von Annotated benutzen, Sie den default-Parameter für Query nicht verwenden dürfen.

Setzen Sie stattdessen den Defaultwert des Funktionsparameters, sonst wäre es inkonsistent.

Zum Beispiel ist das nicht erlaubt:

q: Annotated[str, Query(default="rick")] = "morty"

... denn es wird nicht klar, ob der Defaultwert "rick" oder "morty" sein soll.

Sie würden also (bevorzugt) schreiben:

q: Annotated[str, Query()] = "rick"

In älterem Code werden Sie auch finden:

q: str = Query(default="rick")

Vorzüge von Annotated

Es wird empfohlen, Annotated zu verwenden, statt des Defaultwertes im Funktionsparameter, das ist aus mehreren Gründen besser: 🤓

Der Defaultwert des Funktionsparameters ist der tatsächliche Defaultwert, das spielt generell intuitiver mit Python zusammen. 😌

Sie können die Funktion ohne FastAPI an anderen Stellen aufrufen, und es wird wie erwartet funktionieren. Wenn es einen erforderlichen Parameter gibt (ohne Defaultwert), und Sie führen die Funktion ohne den benötigten Parameter aus, dann wird Ihr Editor Sie das mit einem Fehler wissen lassen, und Python wird sich auch beschweren.

Wenn Sie aber nicht Annotated benutzen und stattdessen die (alte) Variante mit einem Defaultwert, dann müssen Sie, wenn Sie die Funktion ohne FastAPI an anderen Stellen aufrufen, sich daran erinnern, die Argumente der Funktion zu übergeben, damit es richtig funktioniert. Ansonsten erhalten Sie unerwartete Werte (z. B. QueryInfo oder etwas Ähnliches, statt str). Ihr Editor kann ihnen nicht helfen, und Python wird die Funktion ohne Beschwerden ausführen, es sei denn, die Operationen innerhalb lösen einen Fehler aus.

Da Annotated mehrere Metadaten haben kann, können Sie dieselbe Funktion auch mit anderen Tools verwenden, wie etwa Typer. 🚀

Mehr Validierungen hinzufügen

Sie können auch einen Parameter min_length hinzufügen:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[str | None, Query(min_length=3, max_length=50)] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Annotated, Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[Union[str, None], Query(min_length=3, max_length=50)] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Union

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[Union[str, None], Query(min_length=3, max_length=50)] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: str | None = Query(default=None, min_length=3, max_length=50)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from typing import Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Union[str, None] = Query(default=None, min_length=3, max_length=50),
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Reguläre Ausdrücke hinzufügen

Sie können einen Regulären Ausdruck pattern definieren, mit dem der Parameter übereinstimmen muss:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[
        str | None, Query(min_length=3, max_length=50, pattern="^fixedquery$")
    ] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Annotated, Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[
        Union[str, None], Query(min_length=3, max_length=50, pattern="^fixedquery$")
    ] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Union

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[
        Union[str, None], Query(min_length=3, max_length=50, pattern="^fixedquery$")
    ] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: str | None = Query(
        default=None, min_length=3, max_length=50, pattern="^fixedquery$"
    ),
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from typing import Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Union[str, None] = Query(
        default=None, min_length=3, max_length=50, pattern="^fixedquery$"
    ),
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Dieses bestimmte reguläre Suchmuster prüft, ob der erhaltene Parameter-Wert:

  • ^: mit den nachfolgenden Zeichen startet, keine Zeichen davor hat.
  • fixedquery: den exakten Text fixedquery hat.
  • $: danach endet, keine weiteren Zeichen hat als fixedquery.

Wenn Sie sich verloren fühlen bei all diesen „Regulärer Ausdruck“-Konzepten, keine Sorge. Reguläre Ausdrücke sind für viele Menschen ein schwieriges Thema. Sie können auch ohne reguläre Ausdrücke eine ganze Menge machen.

Aber wenn Sie sie brauchen und sie lernen, wissen Sie, dass Sie sie bereits direkt in FastAPI verwenden können.

Pydantic v1 regex statt pattern

Vor Pydantic Version 2 und vor FastAPI Version 0.100.0, war der Name des Parameters regex statt pattern, aber das ist jetzt deprecated.

Sie könnten immer noch Code sehen, der den alten Namen verwendet:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[
        str | None, Query(min_length=3, max_length=50, regex="^fixedquery$")
    ] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Beachten Sie aber, dass das deprecated ist, und zum neuen Namen pattern geändert werden sollte. 🤓

Defaultwerte

Sie können natürlich andere Defaultwerte als None verwenden.

Beispielsweise könnten Sie den q Query-Parameter so deklarieren, dass er eine min_length von 3 hat, und den Defaultwert "fixedquery":

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)] = "fixedquery"):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)] = "fixedquery"):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: str = Query(default="fixedquery", min_length=3)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Hinweis

Ein Parameter ist optional (nicht erforderlich), wenn er irgendeinen Defaultwert, auch None, hat.

Erforderliche Parameter

Wenn wir keine Validierungen oder Metadaten haben, können wir den q Query-Parameter erforderlich machen, indem wir einfach keinen Defaultwert deklarieren, wie in:

q: str

statt:

q: Union[str, None] = None

Aber jetzt deklarieren wir den Parameter mit Query, wie in:

q: Annotated[Union[str, None], Query(min_length=3)] = None
q: Union[str, None] = Query(default=None, min_length=3)

Wenn Sie einen Parameter erforderlich machen wollen, während Sie Query verwenden, deklarieren Sie ebenfalls einfach keinen Defaultwert:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)]):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)]):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: str = Query(min_length=3)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tipp

Beachten Sie, dass, obwohl in diesem Fall Query() der Funktionsparameter-Defaultwert ist, wir nicht default=None zu Query() hinzufügen.

Verwenden Sie bitte trotzdem die Annotated-Version. 😉

Erforderlich mit Ellipse (...)

Es gibt eine Alternative, die explizit deklariert, dass ein Wert erforderlich ist. Sie können als Default das Literal ... setzen:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)] = ...):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)] = ...):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: str = Query(default=..., min_length=3)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Info

Falls Sie das ... bisher noch nicht gesehen haben: Es ist ein spezieller einzelner Wert, Teil von Python und wird „Ellipsis“ genannt (Deutsch: Ellipse).

Es wird von Pydantic und FastAPI verwendet, um explizit zu deklarieren, dass ein Wert erforderlich ist.

Dies wird FastAPI wissen lassen, dass dieser Parameter erforderlich ist.

Erforderlich, kann None sein

Sie können deklarieren, dass ein Parameter None akzeptiert, aber dennoch erforderlich ist. Das zwingt Clients, den Wert zu senden, selbst wenn er None ist.

Um das zu machen, deklarieren Sie, dass None ein gültiger Typ ist, aber verwenden Sie dennoch ... als Default:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(min_length=3)] = ...):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Annotated, Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(min_length=3)] = ...):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Union

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(min_length=3)] = ...):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: str | None = Query(default=..., min_length=3)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from typing import Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Union[str, None] = Query(default=..., min_length=3)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tipp

Pydantic, welches die gesamte Datenvalidierung und Serialisierung in FastAPI antreibt, hat ein spezielles Verhalten, wenn Sie Optional oder Union[Something, None] ohne Defaultwert verwenden, Sie können mehr darüber in der Pydantic-Dokumentation unter Required fields erfahren.

Tipp

Denken Sie daran, dass Sie in den meisten Fällen, wenn etwas erforderlich ist, einfach den Defaultwert weglassen können. Sie müssen also normalerweise ... nicht verwenden.

Query-Parameter-Liste / Mehrere Werte

Wenn Sie einen Query-Parameter explizit mit Query auszeichnen, können Sie ihn auch eine Liste von Werten empfangen lassen, oder anders gesagt, mehrere Werte.

Um zum Beispiel einen Query-Parameter q zu deklarieren, der mehrere Male in der URL vorkommen kann, schreiben Sie:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[list[str] | None, Query()] = None):
    query_items = {"q": q}
    return query_items
from typing import Annotated, Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[Union[list[str], None], Query()] = None):
    query_items = {"q": q}
    return query_items
from typing import List, Union

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[Union[List[str], None], Query()] = None):
    query_items = {"q": q}
    return query_items

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: list[str] | None = Query(default=None)):
    query_items = {"q": q}
    return query_items

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from typing import Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Union[list[str], None] = Query(default=None)):
    query_items = {"q": q}
    return query_items

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from typing import List, Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Union[List[str], None] = Query(default=None)):
    query_items = {"q": q}
    return query_items

Dann, mit einer URL wie:

http://localhost:8000/items/?q=foo&q=bar

bekommen Sie alle q-Query-Parameter-Werte (foo und bar) in einer Python-Liste – list – in ihrer Pfadoperation-Funktion, im Funktionsparameter q, überreicht.

Die Response für diese URL wäre also:

{
  "q": [
    "foo",
    "bar"
  ]
}

Tipp

Um einen Query-Parameter vom Typ list zu deklarieren, wie im Beispiel oben, müssen Sie explizit Query verwenden, sonst würde der Parameter als Requestbody interpretiert werden.

Die interaktive API-Dokumentation wird entsprechend aktualisiert und erlaubt jetzt mehrere Werte.

Query-Parameter-Liste / Mehrere Werte mit Defaults

Und Sie können auch eine Default-liste von Werten definieren, wenn keine übergeben werden:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[list[str], Query()] = ["foo", "bar"]):
    query_items = {"q": q}
    return query_items
from typing import List

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[List[str], Query()] = ["foo", "bar"]):
    query_items = {"q": q}
    return query_items

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: list[str] = Query(default=["foo", "bar"])):
    query_items = {"q": q}
    return query_items

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from typing import List

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: List[str] = Query(default=["foo", "bar"])):
    query_items = {"q": q}
    return query_items

Wenn Sie auf:

http://localhost:8000/items/

gehen, wird der Default für q verwendet: ["foo", "bar"], und als Response erhalten Sie:

{
  "q": [
    "foo",
    "bar"
  ]
}

list alleine verwenden

Sie können auch list direkt verwenden, anstelle von List[str] (oder list[str] in Python 3.9+):

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[list, Query()] = []):
    query_items = {"q": q}
    return query_items
from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[list, Query()] = []):
    query_items = {"q": q}
    return query_items

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: list = Query(default=[])):
    query_items = {"q": q}
    return query_items

Hinweis

Beachten Sie, dass FastAPI in diesem Fall den Inhalt der Liste nicht überprüft.

Zum Beispiel würde List[int] überprüfen (und dokumentieren) dass die Liste Ganzzahlen enthält. list alleine macht das nicht.

Deklarieren von mehr Metadaten

Sie können mehr Informationen zum Parameter hinzufügen.

Diese Informationen werden zur generierten OpenAPI hinzugefügt, und von den Dokumentations-Oberflächen und von externen Tools verwendet.

Hinweis

Beachten Sie, dass verschiedene Tools OpenAPI möglicherweise unterschiedlich gut unterstützen.

Einige könnten noch nicht alle zusätzlichen Informationen anzeigen, die Sie deklariert haben, obwohl in den meisten Fällen geplant ist, das fehlende Feature zu implementieren.

Sie können einen Titel hinzufügen – title:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[str | None, Query(title="Query string", min_length=3)] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Annotated, Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[Union[str, None], Query(title="Query string", min_length=3)] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Union

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[Union[str, None], Query(title="Query string", min_length=3)] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: str | None = Query(default=None, title="Query string", min_length=3),
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from typing import Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Union[str, None] = Query(default=None, title="Query string", min_length=3),
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Und eine Beschreibung – description:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[
        str | None,
        Query(
            title="Query string",
            description="Query string for the items to search in the database that have a good match",
            min_length=3,
        ),
    ] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Annotated, Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[
        Union[str, None],
        Query(
            title="Query string",
            description="Query string for the items to search in the database that have a good match",
            min_length=3,
        ),
    ] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Union

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[
        Union[str, None],
        Query(
            title="Query string",
            description="Query string for the items to search in the database that have a good match",
            min_length=3,
        ),
    ] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: str | None = Query(
        default=None,
        title="Query string",
        description="Query string for the items to search in the database that have a good match",
        min_length=3,
    ),
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from typing import Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Union[str, None] = Query(
        default=None,
        title="Query string",
        description="Query string for the items to search in the database that have a good match",
        min_length=3,
    ),
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Alias-Parameter

Stellen Sie sich vor, der Parameter soll item-query sein.

Wie in:

http://127.0.0.1:8000/items/?item-query=foobaritems

Aber item-query ist kein gültiger Name für eine Variable in Python.

Am ähnlichsten wäre item_query.

Aber Sie möchten dennoch exakt item-query verwenden.

Dann können Sie einen alias deklarieren, und dieser Alias wird verwendet, um den Parameter-Wert zu finden:

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(alias="item-query")] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Annotated, Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(alias="item-query")] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Union

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(q: Annotated[Union[str, None], Query(alias="item-query")] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: str | None = Query(default=None, alias="item-query")):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from typing import Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(q: Union[str, None] = Query(default=None, alias="item-query")):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Parameter als deprecated ausweisen

Nehmen wir an, Sie mögen diesen Parameter nicht mehr.

Sie müssen ihn eine Weile dort belassen, weil Clients ihn benutzen, aber Sie möchten, dass die Dokumentation klar anzeigt, dass er deprecated ist.

In diesem Fall fügen Sie den Parameter deprecated=True zu Query hinzu.

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[
        str | None,
        Query(
            alias="item-query",
            title="Query string",
            description="Query string for the items to search in the database that have a good match",
            min_length=3,
            max_length=50,
            pattern="^fixedquery$",
            deprecated=True,
        ),
    ] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Annotated, Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[
        Union[str, None],
        Query(
            alias="item-query",
            title="Query string",
            description="Query string for the items to search in the database that have a good match",
            min_length=3,
            max_length=50,
            pattern="^fixedquery$",
            deprecated=True,
        ),
    ] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results
from typing import Union

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Annotated[
        Union[str, None],
        Query(
            alias="item-query",
            title="Query string",
            description="Query string for the items to search in the database that have a good match",
            min_length=3,
            max_length=50,
            pattern="^fixedquery$",
            deprecated=True,
        ),
    ] = None,
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: str | None = Query(
        default=None,
        alias="item-query",
        title="Query string",
        description="Query string for the items to search in the database that have a good match",
        min_length=3,
        max_length=50,
        pattern="^fixedquery$",
        deprecated=True,
    ),
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from typing import Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    q: Union[str, None] = Query(
        default=None,
        alias="item-query",
        title="Query string",
        description="Query string for the items to search in the database that have a good match",
        min_length=3,
        max_length=50,
        pattern="^fixedquery$",
        deprecated=True,
    ),
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

Die Dokumentation wird das so anzeigen:

Parameter von OpenAPI ausschließen

Um einen Query-Parameter vom generierten OpenAPI-Schema auszuschließen (und daher von automatischen Dokumentations-Systemen), setzen Sie den Parameter include_in_schema in Query auf False.

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    hidden_query: Annotated[str | None, Query(include_in_schema=False)] = None,
):
    if hidden_query:
        return {"hidden_query": hidden_query}
    else:
        return {"hidden_query": "Not found"}
from typing import Annotated, Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    hidden_query: Annotated[Union[str, None], Query(include_in_schema=False)] = None,
):
    if hidden_query:
        return {"hidden_query": hidden_query}
    else:
        return {"hidden_query": "Not found"}
from typing import Union

from fastapi import FastAPI, Query
from typing_extensions import Annotated

app = FastAPI()


@app.get("/items/")
async def read_items(
    hidden_query: Annotated[Union[str, None], Query(include_in_schema=False)] = None,
):
    if hidden_query:
        return {"hidden_query": hidden_query}
    else:
        return {"hidden_query": "Not found"}

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    hidden_query: str | None = Query(default=None, include_in_schema=False),
):
    if hidden_query:
        return {"hidden_query": hidden_query}
    else:
        return {"hidden_query": "Not found"}

Tipp

Bevorzugen Sie die Annotated-Version, falls möglich.

from typing import Union

from fastapi import FastAPI, Query

app = FastAPI()


@app.get("/items/")
async def read_items(
    hidden_query: Union[str, None] = Query(default=None, include_in_schema=False),
):
    if hidden_query:
        return {"hidden_query": hidden_query}
    else:
        return {"hidden_query": "Not found"}

Zusammenfassung

Sie können zusätzliche Validierungen und Metadaten zu ihren Parametern hinzufügen.

Allgemeine Validierungen und Metadaten:

  • alias
  • title
  • description
  • deprecated

Validierungen spezifisch für Strings:

  • min_length
  • max_length
  • pattern

In diesen Beispielen haben Sie gesehen, wie Sie Validierungen für Strings hinzufügen.

In den nächsten Kapiteln sehen wir, wie man Validierungen für andere Typen hinzufügt, etwa für Zahlen.