python-version: [ "3.14" ]
pydantic-version: [ "pydantic>=2.0.2,<3.0.0" ]
include:
- - os: macos-latest
- python-version: "3.8"
- pydantic-version: "pydantic>=1.10.0,<2.0.0"
- - os: windows-latest
- python-version: "3.8"
- pydantic-version: "pydantic>=2.0.2,<3.0.0"
- coverage: coverage
- os: ubuntu-latest
python-version: "3.9"
pydantic-version: "pydantic>=1.10.0,<2.0.0"
run: uv pip install -r requirements-tests.txt
- name: Install Pydantic
run: uv pip install "${{ matrix.pydantic-version }}"
- # TODO: Remove this once Python 3.8 is no longer supported
- - name: Install older AnyIO in Python 3.8
- if: matrix.python-version == '3.8'
- run: uv pip install "anyio[trio]<4.0.0"
- run: mkdir coverage
- name: Test
run: bash scripts/test.sh
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
- python-version: '3.8'
+ python-version: '3.11'
- name: Setup uv
uses: astral-sh/setup-uv@v7
with:
Um beispielsweise eine weitere Response mit dem Statuscode `404` und einem Pydantic-Modell `Message` zu deklarieren, können Sie schreiben:
-{* ../../docs_src/additional_responses/tutorial001.py hl[18,22] *}
+{* ../../docs_src/additional_responses/tutorial001_py39.py hl[18,22] *}
/// note | Hinweis
Und eine Response mit dem Statuscode `200`, die Ihr `response_model` verwendet, aber ein benutzerdefiniertes Beispiel (`example`) enthält:
-{* ../../docs_src/additional_responses/tutorial003.py hl[20:31] *}
+{* ../../docs_src/additional_responses/tutorial003_py39.py hl[20:31] *}
Es wird alles kombiniert und in Ihre OpenAPI eingebunden und in der API-Dokumentation angezeigt:
Die Datei `main.py` hätte als Inhalt:
-{* ../../docs_src/async_tests/main.py *}
+{* ../../docs_src/async_tests/app_a_py39/main.py *}
Die Datei `test_main.py` hätte die Tests für `main.py`, das könnte jetzt so aussehen:
-{* ../../docs_src/async_tests/test_main.py *}
+{* ../../docs_src/async_tests/app_a_py39/test_main.py *}
## Es ausführen { #run-it }
Der Marker `@pytest.mark.anyio` teilt pytest mit, dass diese Testfunktion asynchron aufgerufen werden soll:
-{* ../../docs_src/async_tests/test_main.py hl[7] *}
+{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[7] *}
/// tip | Tipp
Dann können wir einen `AsyncClient` mit der App erstellen und mit `await` asynchrone Requests an ihn senden.
-{* ../../docs_src/async_tests/test_main.py hl[9:12] *}
+{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[9:12] *}
Das ist das Äquivalent zu:
Angenommen, Sie definieren eine *Pfadoperation* `/items/`:
-{* ../../docs_src/behind_a_proxy/tutorial001_01.py hl[6] *}
+{* ../../docs_src/behind_a_proxy/tutorial001_01_py39.py hl[6] *}
Wenn der Client versucht, zu `/items` zu gehen, würde er standardmäßig zu `/items/` umgeleitet.
Auch wenn Ihr gesamter Code unter der Annahme geschrieben ist, dass es nur `/app` gibt.
-{* ../../docs_src/behind_a_proxy/tutorial001.py hl[6] *}
+{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[6] *}
Und der Proxy würde das **Pfadpräfix** on-the-fly **„entfernen“**, bevor er den <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr> an den Anwendungsserver (wahrscheinlich Uvicorn via FastAPI CLI) übermittelt, dafür sorgend, dass Ihre Anwendung davon überzeugt ist, dass sie unter `/app` bereitgestellt wird, sodass Sie nicht Ihren gesamten Code dahingehend aktualisieren müssen, das Präfix `/api/v1` zu verwenden.
Hier fügen wir ihn, nur zu Demonstrationszwecken, in die Nachricht ein.
-{* ../../docs_src/behind_a_proxy/tutorial001.py hl[8] *}
+{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[8] *}
Wenn Sie Uvicorn dann starten mit:
Falls Sie keine Möglichkeit haben, eine Kommandozeilenoption wie `--root-path` oder ähnlich zu übergeben, können Sie, alternativ dazu, beim Erstellen Ihrer FastAPI-Anwendung den Parameter `root_path` setzen:
-{* ../../docs_src/behind_a_proxy/tutorial002.py hl[3] *}
+{* ../../docs_src/behind_a_proxy/tutorial002_py39.py hl[3] *}
Die Übergabe des `root_path` an `FastAPI` wäre das Äquivalent zur Übergabe der `--root-path`-Kommandozeilenoption an Uvicorn oder Hypercorn.
Zum Beispiel:
-{* ../../docs_src/behind_a_proxy/tutorial003.py hl[4:7] *}
+{* ../../docs_src/behind_a_proxy/tutorial003_py39.py hl[4:7] *}
Erzeugt ein OpenAPI-Schema, wie:
Wenn Sie nicht möchten, dass **FastAPI** einen automatischen Server inkludiert, welcher `root_path` verwendet, können Sie den Parameter `root_path_in_servers=False` verwenden:
-{* ../../docs_src/behind_a_proxy/tutorial004.py hl[9] *}
+{* ../../docs_src/behind_a_proxy/tutorial004_py39.py hl[9] *}
Dann wird er nicht in das OpenAPI-Schema aufgenommen.
Wenn Sie jedoch sicher sind, dass der von Ihnen zurückgegebene Inhalt **mit JSON serialisierbar** ist, können Sie ihn direkt an die Response-Klasse übergeben und die zusätzliche Arbeit vermeiden, die FastAPI hätte, indem es Ihren zurückgegebenen Inhalt durch den `jsonable_encoder` leitet, bevor es ihn an die Response-Klasse übergibt.
-{* ../../docs_src/custom_response/tutorial001b.py hl[2,7] *}
+{* ../../docs_src/custom_response/tutorial001b_py39.py hl[2,7] *}
/// info | Info
* Importieren Sie `HTMLResponse`.
* Übergeben Sie `HTMLResponse` als den Parameter `response_class` Ihres *Pfadoperation-Dekorators*.
-{* ../../docs_src/custom_response/tutorial002.py hl[2,7] *}
+{* ../../docs_src/custom_response/tutorial002_py39.py hl[2,7] *}
/// info | Info
Das gleiche Beispiel von oben, das eine `HTMLResponse` zurückgibt, könnte so aussehen:
-{* ../../docs_src/custom_response/tutorial003.py hl[2,7,19] *}
+{* ../../docs_src/custom_response/tutorial003_py39.py hl[2,7,19] *}
/// warning | Achtung
Es könnte zum Beispiel so etwas sein:
-{* ../../docs_src/custom_response/tutorial004.py hl[7,21,23] *}
+{* ../../docs_src/custom_response/tutorial004_py39.py hl[7,21,23] *}
In diesem Beispiel generiert die Funktion `generate_html_response()` bereits eine `Response` und gibt sie zurück, anstatt das HTML in einem `str` zurückzugeben.
FastAPI (eigentlich Starlette) fügt automatisch einen Content-Length-Header ein. Außerdem wird es einen Content-Type-Header einfügen, der auf dem media_type basiert, und für Texttypen einen Zeichensatz (charset) anfügen.
-{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
+{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
### `HTMLResponse` { #htmlresponse }
Nimmt Text oder Bytes entgegen und gibt eine Plain-Text-Response zurück.
-{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *}
+{* ../../docs_src/custom_response/tutorial005_py39.py hl[2,7,9] *}
### `JSONResponse` { #jsonresponse }
///
-{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *}
+{* ../../docs_src/custom_response/tutorial001_py39.py hl[2,7] *}
/// tip | Tipp
Sie können eine `RedirectResponse` direkt zurückgeben:
-{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *}
+{* ../../docs_src/custom_response/tutorial006_py39.py hl[2,9] *}
---
Oder Sie können sie im Parameter `response_class` verwenden:
-{* ../../docs_src/custom_response/tutorial006b.py hl[2,7,9] *}
+{* ../../docs_src/custom_response/tutorial006b_py39.py hl[2,7,9] *}
Wenn Sie das tun, können Sie die URL direkt von Ihrer *Pfadoperation*-Funktion zurückgeben.
Sie können den Parameter `status_code` auch in Kombination mit dem Parameter `response_class` verwenden:
-{* ../../docs_src/custom_response/tutorial006c.py hl[2,7,9] *}
+{* ../../docs_src/custom_response/tutorial006c_py39.py hl[2,7,9] *}
### `StreamingResponse` { #streamingresponse }
Nimmt einen asynchronen Generator oder einen normalen Generator/Iterator und streamt den Responsebody.
-{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *}
+{* ../../docs_src/custom_response/tutorial007_py39.py hl[2,14] *}
#### Verwendung von `StreamingResponse` mit dateiartigen Objekten { #using-streamingresponse-with-file-like-objects }
Das umfasst viele Bibliotheken zur Interaktion mit Cloud-Speicher, Videoverarbeitung und anderen.
-{* ../../docs_src/custom_response/tutorial008.py hl[2,10:12,14] *}
+{* ../../docs_src/custom_response/tutorial008_py39.py hl[2,10:12,14] *}
1. Das ist die Generatorfunktion. Es handelt sich um eine „Generatorfunktion“, da sie `yield`-Anweisungen enthält.
2. Durch die Verwendung eines `with`-Blocks stellen wir sicher, dass das dateiartige Objekt geschlossen wird, nachdem die Generatorfunktion fertig ist. Also, nachdem sie mit dem Senden der Response fertig ist.
Datei-Responses enthalten die entsprechenden `Content-Length`-, `Last-Modified`- und `ETag`-Header.
-{* ../../docs_src/custom_response/tutorial009.py hl[2,10] *}
+{* ../../docs_src/custom_response/tutorial009_py39.py hl[2,10] *}
Sie können auch den Parameter `response_class` verwenden:
-{* ../../docs_src/custom_response/tutorial009b.py hl[2,8,10] *}
+{* ../../docs_src/custom_response/tutorial009b_py39.py hl[2,8,10] *}
In diesem Fall können Sie den Dateipfad direkt von Ihrer *Pfadoperation*-Funktion zurückgeben.
Sie könnten eine `CustomORJSONResponse` erstellen. Das Wichtigste, was Sie tun müssen, ist, eine `Response.render(content)`-Methode zu erstellen, die den Inhalt als `bytes` zurückgibt:
-{* ../../docs_src/custom_response/tutorial009c.py hl[9:14,17] *}
+{* ../../docs_src/custom_response/tutorial009c_py39.py hl[9:14,17] *}
Statt:
Im folgenden Beispiel verwendet **FastAPI** standardmäßig `ORJSONResponse` in allen *Pfadoperationen*, anstelle von `JSONResponse`.
-{* ../../docs_src/custom_response/tutorial010.py hl[2,4] *}
+{* ../../docs_src/custom_response/tutorial010_py39.py hl[2,4] *}
/// tip | Tipp
Wir erstellen eine asynchrone Funktion `lifespan()` mit `yield` wie folgt:
-{* ../../docs_src/events/tutorial003.py hl[16,19] *}
+{* ../../docs_src/events/tutorial003_py39.py hl[16,19] *}
Hier simulieren wir den langsamen *Startup*, das Laden des Modells, indem wir die (Fake-)Modellfunktion vor dem `yield` in das <abbr title="Dictionary – Zuordnungstabelle: In anderen Sprachen auch Hash, Map, Objekt, Assoziatives Array genannt">Dictionary</abbr> mit Modellen für maschinelles Lernen einfügen. Dieser Code wird ausgeführt, **bevor** die Anwendung **beginnt, Requests entgegenzunehmen**, während des *Startups*.
Das Erste, was auffällt, ist, dass wir eine asynchrone Funktion mit `yield` definieren. Das ist sehr ähnlich zu Abhängigkeiten mit `yield`.
-{* ../../docs_src/events/tutorial003.py hl[14:19] *}
+{* ../../docs_src/events/tutorial003_py39.py hl[14:19] *}
Der erste Teil der Funktion, vor dem `yield`, wird ausgeführt **bevor** die Anwendung startet.
Dadurch wird die Funktion in einen sogenannten „**asynchronen Kontextmanager**“ umgewandelt.
-{* ../../docs_src/events/tutorial003.py hl[1,13] *}
+{* ../../docs_src/events/tutorial003_py39.py hl[1,13] *}
Ein **Kontextmanager** in Python ist etwas, das Sie in einer `with`-Anweisung verwenden können, zum Beispiel kann `open()` als Kontextmanager verwendet werden:
Der Parameter `lifespan` der `FastAPI`-App benötigt einen **asynchronen Kontextmanager**, wir können ihm also unseren neuen asynchronen Kontextmanager `lifespan` übergeben.
-{* ../../docs_src/events/tutorial003.py hl[22] *}
+{* ../../docs_src/events/tutorial003_py39.py hl[22] *}
## Alternative Events (<abbr title="veraltet, obsolet: Es soll nicht mehr verwendet werden">deprecatet</abbr>) { #alternative-events-deprecated }
Um eine Funktion hinzuzufügen, die vor dem Start der Anwendung ausgeführt werden soll, deklarieren Sie diese mit dem Event `startup`:
-{* ../../docs_src/events/tutorial001.py hl[8] *}
+{* ../../docs_src/events/tutorial001_py39.py hl[8] *}
In diesem Fall initialisiert die Eventhandler-Funktion `startup` die „Datenbank“ der Items (nur ein `dict`) mit einigen Werten.
Um eine Funktion hinzuzufügen, die beim Shutdown der Anwendung ausgeführt werden soll, deklarieren Sie sie mit dem Event `shutdown`:
-{* ../../docs_src/events/tutorial002.py hl[6] *}
+{* ../../docs_src/events/tutorial002_py39.py hl[6] *}
Hier schreibt die `shutdown`-Eventhandler-Funktion eine Textzeile `"Application shutdown"` in eine Datei `log.txt`.
Wir könnten das OpenAPI-JSON in eine Datei `openapi.json` herunterladen und dann mit einem Skript wie dem folgenden **den präfixierten Tag entfernen**:
-{* ../../docs_src/generate_clients/tutorial004.py *}
+{* ../../docs_src/generate_clients/tutorial004_py39.py *}
//// tab | Node.js
Alle eingehenden Requests an `http` oder `ws` werden stattdessen an das sichere Schema umgeleitet.
-{* ../../docs_src/advanced_middleware/tutorial001.py hl[2,6] *}
+{* ../../docs_src/advanced_middleware/tutorial001_py39.py hl[2,6] *}
## `TrustedHostMiddleware` { #trustedhostmiddleware }
Erzwingt, dass alle eingehenden Requests einen korrekt gesetzten `Host`-Header haben, um sich vor HTTP-Host-Header-Angriffen zu schützen.
-{* ../../docs_src/advanced_middleware/tutorial002.py hl[2,6:8] *}
+{* ../../docs_src/advanced_middleware/tutorial002_py39.py hl[2,6:8] *}
Die folgenden Argumente werden unterstützt:
Diese Middleware verarbeitet sowohl Standard- als auch Streaming-Responses.
-{* ../../docs_src/advanced_middleware/tutorial003.py hl[2,6] *}
+{* ../../docs_src/advanced_middleware/tutorial003_py39.py hl[2,6] *}
Die folgenden Argumente werden unterstützt:
Wenn Sie eine **FastAPI**-Anwendung erstellen, gibt es ein `webhooks`-Attribut, das Sie verwenden können, um *Webhooks* zu definieren, genauso wie Sie *Pfadoperationen* definieren würden, zum Beispiel mit `@app.webhooks.post()`.
-{* ../../docs_src/openapi_webhooks/tutorial001.py hl[9:13,36:53] *}
+{* ../../docs_src/openapi_webhooks/tutorial001_py39.py hl[9:13,36:53] *}
Die von Ihnen definierten Webhooks landen im **OpenAPI**-Schema und der automatischen **Dokumentations-Oberfläche**.
Sie müssten sicherstellen, dass sie für jede Operation eindeutig ist.
-{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *}
### Verwendung des Namens der *Pfadoperation-Funktion* als operationId { #using-the-path-operation-function-name-as-the-operationid }
Sie sollten dies tun, nachdem Sie alle Ihre *Pfadoperationen* hinzugefügt haben.
-{* ../../docs_src/path_operation_advanced_configuration/tutorial002.py hl[2, 12:21, 24] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py39.py hl[2, 12:21, 24] *}
/// tip | Tipp
Um eine *Pfadoperation* aus dem generierten OpenAPI-Schema (und damit aus den automatischen Dokumentationssystemen) auszuschließen, verwenden Sie den Parameter `include_in_schema` und setzen Sie ihn auf `False`:
-{* ../../docs_src/path_operation_advanced_configuration/tutorial003.py hl[6] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py39.py hl[6] *}
## Fortgeschrittene Beschreibung mittels Docstring { #advanced-description-from-docstring }
Dieses `openapi_extra` kann beispielsweise hilfreich sein, um [OpenAPI-Erweiterungen](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions) zu deklarieren:
-{* ../../docs_src/path_operation_advanced_configuration/tutorial005.py hl[6] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py39.py hl[6] *}
Wenn Sie die automatische API-Dokumentation öffnen, wird Ihre Erweiterung am Ende der spezifischen *Pfadoperation* angezeigt.
Das könnte man mit `openapi_extra` machen:
-{* ../../docs_src/path_operation_advanced_configuration/tutorial006.py hl[19:36, 39:40] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *}
In diesem Beispiel haben wir kein Pydantic-Modell deklariert. Tatsächlich wird der Requestbody nicht einmal als JSON <abbr title="von einem einfachen Format, wie Bytes, in Python-Objekte konvertieren">geparst</abbr>, sondern direkt als `bytes` gelesen und die Funktion `magic_data_reader()` wäre dafür verantwortlich, ihn in irgendeiner Weise zu parsen.
Anschließend können Sie den `status_code` in diesem *vorübergehenden* <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr>-Objekt festlegen.
-{* ../../docs_src/response_change_status_code/tutorial001.py hl[1,9,12] *}
+{* ../../docs_src/response_change_status_code/tutorial001_py39.py hl[1,9,12] *}
Und dann können Sie jedes benötigte Objekt zurückgeben, wie Sie es normalerweise tun würden (ein `dict`, ein Datenbankmodell usw.).
Und dann können Sie Cookies in diesem *vorübergehenden* <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr>-Objekt setzen.
-{* ../../docs_src/response_cookies/tutorial002.py hl[1, 8:9] *}
+{* ../../docs_src/response_cookies/tutorial002_py39.py hl[1, 8:9] *}
Anschließend können Sie wie gewohnt jedes gewünschte Objekt zurückgeben (ein `dict`, ein Datenbankmodell, usw.).
Setzen Sie dann Cookies darin und geben Sie sie dann zurück:
-{* ../../docs_src/response_cookies/tutorial001.py hl[10:12] *}
+{* ../../docs_src/response_cookies/tutorial001_py39.py hl[10:12] *}
/// tip | Tipp
Sie könnten Ihren XML-Inhalt als String in eine `Response` einfügen und sie zurückgeben:
-{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
+{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
## Anmerkungen { #notes }
Und dann können Sie Header in diesem *vorübergehenden* <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr>-Objekt festlegen.
-{* ../../docs_src/response_headers/tutorial002.py hl[1, 7:8] *}
+{* ../../docs_src/response_headers/tutorial002_py39.py hl[1, 7:8] *}
Anschließend können Sie wie gewohnt jedes gewünschte Objekt zurückgeben (ein `dict`, ein Datenbankmodell, usw.).
Erstellen Sie eine Response wie in [Eine Response direkt zurückgeben](response-directly.md){.internal-link target=_blank} beschrieben und übergeben Sie die Header als zusätzlichen Parameter:
-{* ../../docs_src/response_headers/tutorial001.py hl[10:12] *}
+{* ../../docs_src/response_headers/tutorial001_py39.py hl[10:12] *}
/// note | Technische Details
//// tab | Pydantic v2
-{* ../../docs_src/settings/tutorial001.py hl[2,5:8,11] *}
+{* ../../docs_src/settings/tutorial001_py39.py hl[2,5:8,11] *}
////
///
-{* ../../docs_src/settings/tutorial001_pv1.py hl[2,5:8,11] *}
+{* ../../docs_src/settings/tutorial001_pv1_py39.py hl[2,5:8,11] *}
////
Dann können Sie das neue `settings`-Objekt in Ihrer Anwendung verwenden:
-{* ../../docs_src/settings/tutorial001.py hl[18:20] *}
+{* ../../docs_src/settings/tutorial001_py39.py hl[18:20] *}
### Den Server ausführen { #run-the-server }
Sie könnten beispielsweise eine Datei `config.py` haben mit:
-{* ../../docs_src/settings/app01/config.py *}
+{* ../../docs_src/settings/app01_py39/config.py *}
Und dann verwenden Sie diese in einer Datei `main.py`:
-{* ../../docs_src/settings/app01/main.py hl[3,11:13] *}
+{* ../../docs_src/settings/app01_py39/main.py hl[3,11:13] *}
/// tip | Tipp
Erstellen Sie zunächst die Hauptanwendung **FastAPI** und deren *Pfadoperationen*:
-{* ../../docs_src/sub_applications/tutorial001.py hl[3, 6:8] *}
+{* ../../docs_src/sub_applications/tutorial001_py39.py hl[3, 6:8] *}
### Unteranwendung { #sub-application }
Diese Unteranwendung ist nur eine weitere Standard-FastAPI-Anwendung, aber diese wird „gemountet“:
-{* ../../docs_src/sub_applications/tutorial001.py hl[11, 14:16] *}
+{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 14:16] *}
### Die Unteranwendung mounten { #mount-the-sub-application }
In diesem Fall wird sie im Pfad `/subapi` gemountet:
-{* ../../docs_src/sub_applications/tutorial001.py hl[11, 19] *}
+{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 19] *}
### Die automatische API-Dokumentation testen { #check-the-automatic-api-docs }
* Deklarieren Sie einen `<abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Request</abbr>`-Parameter in der *Pfadoperation*, welcher ein Template zurückgibt.
* Verwenden Sie die von Ihnen erstellten `templates`, um eine `TemplateResponse` zu rendern und zurückzugeben, übergeben Sie den Namen des Templates, das Requestobjekt und ein „Kontext“-<abbr title="Dictionary – Zuordnungstabelle: In anderen Sprachen auch Hash, Map, Objekt, Assoziatives Array genannt">Dictionary</abbr> mit Schlüssel-Wert-Paaren, die innerhalb des Jinja2-Templates verwendet werden sollen.
-{* ../../docs_src/templates/tutorial001.py hl[4,11,15:18] *}
+{* ../../docs_src/templates/tutorial001_py39.py hl[4,11,15:18] *}
/// note | Hinweis
Wenn Sie `lifespan` in Ihren Tests ausführen müssen, können Sie den `TestClient` mit einer `with`-Anweisung verwenden:
-{* ../../docs_src/app_testing/tutorial004.py hl[9:15,18,27:28,30:32,41:43] *}
+{* ../../docs_src/app_testing/tutorial004_py39.py hl[9:15,18,27:28,30:32,41:43] *}
Sie können mehr Details unter [„Lifespan in Tests ausführen in der offiziellen Starlette-Dokumentation.“](https://www.starlette.dev/lifespan/#running-lifespan-in-tests) nachlesen.
Für die deprecateten Events <abbr title="Hochfahren">`startup`</abbr> und <abbr title="Herunterfahren">`shutdown`</abbr> können Sie den `TestClient` wie folgt verwenden:
-{* ../../docs_src/app_testing/tutorial003.py hl[9:12,20:24] *}
+{* ../../docs_src/app_testing/tutorial003_py39.py hl[9:12,20:24] *}
Dazu verwenden Sie den `TestClient` in einer `with`-Anweisung, eine Verbindung zum WebSocket herstellend:
-{* ../../docs_src/app_testing/tutorial002.py hl[27:31] *}
+{* ../../docs_src/app_testing/tutorial002_py39.py hl[27:31] *}
/// note | Hinweis
Dazu müssen Sie direkt auf den Request zugreifen.
-{* ../../docs_src/using_request_directly/tutorial001.py hl[1,7:8] *}
+{* ../../docs_src/using_request_directly/tutorial001_py39.py hl[1,7:8] *}
Durch die Deklaration eines *Pfadoperation-Funktionsparameters*, dessen Typ der `Request` ist, weiß **FastAPI**, dass es den `Request` diesem Parameter übergeben soll.
Aber es ist der einfachste Weg, sich auf die Serverseite von WebSockets zu konzentrieren und ein funktionierendes Beispiel zu haben:
-{* ../../docs_src/websockets/tutorial001.py hl[2,6:38,41:43] *}
+{* ../../docs_src/websockets/tutorial001_py39.py hl[2,6:38,41:43] *}
## Einen `websocket` erstellen { #create-a-websocket }
Erstellen Sie in Ihrer **FastAPI**-Anwendung einen `websocket`:
-{* ../../docs_src/websockets/tutorial001.py hl[1,46:47] *}
+{* ../../docs_src/websockets/tutorial001_py39.py hl[1,46:47] *}
/// note | Technische Details
In Ihrer WebSocket-Route können Sie Nachrichten `await`en und Nachrichten senden.
-{* ../../docs_src/websockets/tutorial001.py hl[48:52] *}
+{* ../../docs_src/websockets/tutorial001_py39.py hl[48:52] *}
Sie können Binär-, Text- und JSON-Daten empfangen und senden.
Und dann mounten Sie das auf einem Pfad.
-{* ../../docs_src/wsgi/tutorial001.py hl[2:3,3] *}
+{* ../../docs_src/wsgi/tutorial001_py39.py hl[2:3,3] *}
## Es testen { #check-it }
Zum Beispiel:
-{* ../../docs_src/conditional_openapi/tutorial001.py hl[6,11] *}
+{* ../../docs_src/conditional_openapi/tutorial001_py39.py hl[6,11] *}
Hier deklarieren wir die Einstellung `openapi_url` mit dem gleichen Defaultwert `"/openapi.json"`.
Sie können sie jedoch deaktivieren, indem Sie `syntaxHighlight` auf `False` setzen:
-{* ../../docs_src/configure_swagger_ui/tutorial001.py hl[3] *}
+{* ../../docs_src/configure_swagger_ui/tutorial001_py39.py hl[3] *}
... und dann zeigt die Swagger-Oberfläche die Syntaxhervorhebung nicht mehr an:
Auf die gleiche Weise könnten Sie das Theme der Syntaxhervorhebung mit dem Schlüssel `"syntaxHighlight.theme"` festlegen (beachten Sie, dass er einen Punkt in der Mitte hat):
-{* ../../docs_src/configure_swagger_ui/tutorial002.py hl[3] *}
+{* ../../docs_src/configure_swagger_ui/tutorial002_py39.py hl[3] *}
Obige Konfiguration würde das Theme für die Farbe der Syntaxhervorhebung ändern:
Um beispielsweise `deepLinking` zu deaktivieren, könnten Sie folgende Einstellungen an `swagger_ui_parameters` übergeben:
-{* ../../docs_src/configure_swagger_ui/tutorial003.py hl[3] *}
+{* ../../docs_src/configure_swagger_ui/tutorial003_py39.py hl[3] *}
## Andere Parameter der Swagger-Oberfläche { #other-swagger-ui-parameters }
Um diese zu deaktivieren, setzen Sie deren URLs beim Erstellen Ihrer `FastAPI`-App auf `None`:
-{* ../../docs_src/custom_docs_ui/tutorial001.py hl[8] *}
+{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[8] *}
### Die benutzerdefinierten Dokumentationen hinzufügen { #include-the-custom-docs }
Und ähnlich für ReDoc ...
-{* ../../docs_src/custom_docs_ui/tutorial001.py hl[2:6,11:19,22:24,27:33] *}
+{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[2:6,11:19,22:24,27:33] *}
/// tip | Tipp
Um nun testen zu können, ob alles funktioniert, erstellen Sie eine *Pfadoperation*:
-{* ../../docs_src/custom_docs_ui/tutorial001.py hl[36:38] *}
+{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[36:38] *}
### Es testen { #test-it }
* Importieren Sie `StaticFiles`.
* „Mounten“ Sie eine `StaticFiles()`-Instanz in einem bestimmten Pfad.
-{* ../../docs_src/custom_docs_ui/tutorial002.py hl[7,11] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[7,11] *}
### Die statischen Dateien testen { #test-the-static-files }
Um sie zu deaktivieren, setzen Sie deren URLs beim Erstellen Ihrer `FastAPI`-App auf `None`:
-{* ../../docs_src/custom_docs_ui/tutorial002.py hl[9] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[9] *}
### Die benutzerdefinierten Dokumentationen für statische Dateien hinzufügen { #include-the-custom-docs-for-static-files }
Und ähnlich für ReDoc ...
-{* ../../docs_src/custom_docs_ui/tutorial002.py hl[2:6,14:22,25:27,30:36] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[2:6,14:22,25:27,30:36] *}
/// tip | Tipp
Um nun testen zu können, ob alles funktioniert, erstellen Sie eine *Pfadoperation*:
-{* ../../docs_src/custom_docs_ui/tutorial002.py hl[39:41] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[39:41] *}
### Benutzeroberfläche mit statischen Dateien testen { #test-static-files-ui }
Schreiben Sie zunächst wie gewohnt Ihre ganze **FastAPI**-Anwendung:
-{* ../../docs_src/extending_openapi/tutorial001.py hl[1,4,7:9] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[1,4,7:9] *}
### Das OpenAPI-Schema generieren { #generate-the-openapi-schema }
Verwenden Sie dann dieselbe Hilfsfunktion, um das OpenAPI-Schema innerhalb einer `custom_openapi()`-Funktion zu generieren:
-{* ../../docs_src/extending_openapi/tutorial001.py hl[2,15:21] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[2,15:21] *}
### Das OpenAPI-Schema ändern { #modify-the-openapi-schema }
Jetzt können Sie die ReDoc-Erweiterung hinzufügen und dem `info`-„Objekt“ im OpenAPI-Schema ein benutzerdefiniertes `x-logo` hinzufügen:
-{* ../../docs_src/extending_openapi/tutorial001.py hl[22:24] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[22:24] *}
### Zwischenspeichern des OpenAPI-Schemas { #cache-the-openapi-schema }
Es wird nur einmal generiert und dann wird dasselbe zwischengespeicherte Schema für die nächsten <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Requests</abbr> verwendet.
-{* ../../docs_src/extending_openapi/tutorial001.py hl[13:14,25:26] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[13:14,25:26] *}
### Die Methode überschreiben { #override-the-method }
Jetzt können Sie die Methode `.openapi()` durch Ihre neue Funktion ersetzen.
-{* ../../docs_src/extending_openapi/tutorial001.py hl[29] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[29] *}
### Es testen { #check-it }
Hier ist eine kleine Vorschau, wie Sie Strawberry mit FastAPI integrieren können:
-{* ../../docs_src/graphql/tutorial001.py hl[3,22,25] *}
+{* ../../docs_src/graphql/tutorial001_py39.py hl[3,22,25] *}
Weitere Informationen zu Strawberry finden Sie in der <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry-Dokumentation</a>.
- 🔍 [Pydantic](https://docs.pydantic.dev), verwendet von FastAPI, für die Datenvalidierung und das Einstellungsmanagement.
- 💾 [PostgreSQL](https://www.postgresql.org) als SQL-Datenbank.
- 🚀 [React](https://react.dev) für das Frontend.
- - 💃 Verwendung von TypeScript, Hooks, [Vite](https://vitejs.dev) und anderen Teilen eines modernen Frontend-Stacks.
+ - 💃 Verwendung von TypeScript, Hooks, Vite und anderen Teilen eines modernen Frontend-Stacks.
- 🎨 [Tailwind CSS](https://tailwindcss.com) und [shadcn/ui](https://ui.shadcn.com) für die Frontend-Komponenten.
- 🤖 Ein automatisch generierter Frontend-Client.
- 🧪 [Playwright](https://playwright.dev) für End-to-End-Tests.
Fangen wir mit einem einfachen Beispiel an:
-{* ../../docs_src/python_types/tutorial001.py *}
+{* ../../docs_src/python_types/tutorial001_py39.py *}
Dieses Programm gibt aus:
* Nimmt einen `first_name` und `last_name`.
* Schreibt den ersten Buchstaben eines jeden Wortes groß, mithilfe von `title()`.
-* <abbr title="Füge zu einer Einheit zusammen, eins nach dem anderen.">Verkettet</abbr> sie mit einem Leerzeichen in der Mitte.
+* <abbr title="Fügt sie zu einer Einheit zusammen. Mit dem Inhalt des einen nach dem anderen.">Verkettet</abbr> sie mit einem Leerzeichen in der Mitte.
-{* ../../docs_src/python_types/tutorial001.py hl[2] *}
+{* ../../docs_src/python_types/tutorial001_py39.py hl[2] *}
### Es bearbeiten { #edit-it }
Das sind die „Typhinweise“:
-{* ../../docs_src/python_types/tutorial002.py hl[1] *}
+{* ../../docs_src/python_types/tutorial002_py39.py hl[1] *}
Das ist nicht das gleiche wie das Deklarieren von Defaultwerten, wie es hier der Fall ist:
Sehen Sie sich diese Funktion an, sie hat bereits Typhinweise:
-{* ../../docs_src/python_types/tutorial003.py hl[1] *}
+{* ../../docs_src/python_types/tutorial003_py39.py hl[1] *}
Da der Editor die Typen der Variablen kennt, erhalten Sie nicht nur Code-Vervollständigung, sondern auch eine Fehlerprüfung:
Jetzt, da Sie wissen, dass Sie das reparieren müssen, konvertieren Sie `age` mittels `str(age)` in einen String:
-{* ../../docs_src/python_types/tutorial004.py hl[2] *}
+{* ../../docs_src/python_types/tutorial004_py39.py hl[2] *}
## Deklarieren von Typen { #declaring-types }
* `bool`
* `bytes`
-{* ../../docs_src/python_types/tutorial005.py hl[1] *}
+{* ../../docs_src/python_types/tutorial005_py39.py hl[1] *}
### Generische Typen mit Typ-Parametern { #generic-types-with-type-parameters }
Definieren wir zum Beispiel eine Variable, die eine `list` von `str` – eine Liste von Strings – sein soll.
-//// tab | Python 3.9+
-
Deklarieren Sie die Variable mit der gleichen Doppelpunkt-Syntax (`:`).
Als Typ nehmen Sie `list`.
Da die Liste ein Typ ist, welcher innere Typen enthält, werden diese von eckigen Klammern umfasst:
-```Python hl_lines="1"
-{!> ../../docs_src/python_types/tutorial006_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-Von `typing` importieren Sie `List` (mit Großbuchstaben `L`):
-
-```Python hl_lines="1"
-{!> ../../docs_src/python_types/tutorial006.py!}
-```
-
-Deklarieren Sie die Variable mit der gleichen Doppelpunkt-Syntax (`:`).
-
-Als Typ nehmen Sie das `List`, das Sie von `typing` importiert haben.
-
-Da die Liste ein Typ ist, welcher innere Typen enthält, werden diese von eckigen Klammern umfasst:
-
-```Python hl_lines="4"
-{!> ../../docs_src/python_types/tutorial006.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial006_py39.py hl[1] *}
/// info | Info
Die inneren Typen in den eckigen Klammern werden als „Typ-Parameter“ bezeichnet.
-In diesem Fall ist `str` der Typ-Parameter, der an `List` übergeben wird (oder `list` in Python 3.9 und darüber).
+In diesem Fall ist `str` der Typ-Parameter, der an `list` übergeben wird.
///
Das bedeutet: Die Variable `items` ist eine Liste – `list` – und jedes der Elemente in dieser Liste ist ein String – `str`.
-/// tip | Tipp
-
-Wenn Sie Python 3.9 oder höher verwenden, müssen Sie `List` nicht von `typing` importieren, Sie können stattdessen den regulären `list`-Typ verwenden.
-
-///
-
Auf diese Weise kann Ihr Editor Sie auch bei der Bearbeitung von Einträgen aus der Liste unterstützen:
<img src="/img/python-types/image05.png">
Das Gleiche gilt für die Deklaration eines Tupels – `tuple` – und einer Menge – `set`:
-//// tab | Python 3.9+
-
-```Python hl_lines="1"
-{!> ../../docs_src/python_types/tutorial007_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial007.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial007_py39.py hl[1] *}
Das bedeutet:
Der zweite Typ-Parameter ist für die Werte des `dict`:
-//// tab | Python 3.9+
-
-```Python hl_lines="1"
-{!> ../../docs_src/python_types/tutorial008_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial008.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial008_py39.py hl[1] *}
Das bedeutet:
In Python 3.6 und höher (inklusive Python 3.10) können Sie den `Union`-Typ von `typing` verwenden und die möglichen Typen innerhalb der eckigen Klammern auflisten.
-In Python 3.10 gibt es zusätzlich eine **neue Syntax**, die es erlaubt, die möglichen Typen getrennt von einem <abbr title='Allgemein: „oder“. In anderem Zusammenhang auch „Bitweises ODER“, aber letztere Bedeutung ist hier nicht relevant'>vertikalen Balken (`|`)</abbr> aufzulisten.
+In Python 3.10 gibt es zusätzlich eine **neue Syntax**, die es erlaubt, die möglichen Typen getrennt von einem <abbr title='auch „bitweiser Oder-Operator“ genannt, aber diese Bedeutung ist hier nicht relevant'>vertikalen Balken (`|`)</abbr> aufzulisten.
//// tab | Python 3.10+
////
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial008b.py!}
+{!> ../../docs_src/python_types/tutorial008b_py39.py!}
```
////
In Python 3.6 und darüber (inklusive Python 3.10) können Sie das deklarieren, indem Sie `Optional` vom `typing` Modul importieren und verwenden.
```Python hl_lines="1 4"
-{!../../docs_src/python_types/tutorial009.py!}
+{!../../docs_src/python_types/tutorial009_py39.py!}
```
Wenn Sie `Optional[str]` anstelle von nur `str` verwenden, wird Ihr Editor Ihnen dabei helfen, Fehler zu erkennen, bei denen Sie annehmen könnten, dass ein Wert immer eine String (`str`) ist, obwohl er auch `None` sein könnte.
////
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial009.py!}
+{!> ../../docs_src/python_types/tutorial009_py39.py!}
```
////
-//// tab | Python 3.8+ Alternative
+//// tab | Python 3.9+ Alternative
```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial009b.py!}
+{!> ../../docs_src/python_types/tutorial009b_py39.py!}
```
////
Ich denke, `Union[SomeType, None]` ist expliziter bezüglich seiner Bedeutung.
-Es geht nur um Wörter und Namen. Aber diese Worte können beeinflussen, wie Sie und Ihre Teamkollegen über den Code denken.
+Es geht nur um Worte und Namen. Aber diese Worte können beeinflussen, wie Sie und Ihre Teamkollegen über den Code denken.
Nehmen wir zum Beispiel diese Funktion:
-{* ../../docs_src/python_types/tutorial009c.py hl[1,4] *}
+{* ../../docs_src/python_types/tutorial009c_py39.py hl[1,4] *}
Der Parameter `name` ist definiert als `Optional[str]`, aber er ist **nicht optional**, Sie können die Funktion nicht ohne diesen Parameter aufrufen:
* `set`
* `dict`
-Verwenden Sie für den Rest, wie unter Python 3.8, das `typing`-Modul:
+Und ebenso wie bei früheren Python-Versionen, aus dem `typing`-Modul:
* `Union`
-* `Optional` (so wie unter Python 3.8)
+* `Optional`
* ... und andere.
-In Python 3.10 können Sie als Alternative zu den Generics `Union` und `Optional` den <abbr title='Allgemein: „oder“. In anderem Zusammenhang auch „Bitweises ODER“, aber letztere Bedeutung ist hier nicht relevant'>vertikalen Balken (`|`)</abbr> verwenden, um Vereinigungen von Typen zu deklarieren, das ist besser und einfacher.
+In Python 3.10 können Sie als Alternative zu den Generics `Union` und `Optional` den <abbr title='auch „bitweiser Oder-Operator“ genannt, aber diese Bedeutung ist hier nicht relevant'>vertikalen Balken (`|`)</abbr> verwenden, um Vereinigungen von Typen zu deklarieren, das ist besser und einfacher.
////
* `set`
* `dict`
-Verwenden Sie für den Rest, wie unter Python 3.8, das `typing`-Modul:
+Und Generics aus dem `typing`-Modul:
* `Union`
* `Optional`
////
-//// tab | Python 3.8+
-
-* `List`
-* `Tuple`
-* `Set`
-* `Dict`
-* `Union`
-* `Optional`
-* ... und andere.
-
-////
-
### Klassen als Typen { #classes-as-types }
Sie können auch eine Klasse als Typ einer Variablen deklarieren.
Nehmen wir an, Sie haben eine Klasse `Person`, mit einem Namen:
-{* ../../docs_src/python_types/tutorial010.py hl[1:3] *}
+{* ../../docs_src/python_types/tutorial010_py39.py hl[1:3] *}
Dann können Sie eine Variable vom Typ `Person` deklarieren:
-{* ../../docs_src/python_types/tutorial010.py hl[6] *}
+{* ../../docs_src/python_types/tutorial010_py39.py hl[6] *}
Und wiederum bekommen Sie die volle Editor-Unterstützung:
Ein Beispiel aus der offiziellen Pydantic Dokumentation:
-//// tab | Python 3.10+
-
-```Python
-{!> ../../docs_src/python_types/tutorial011_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python
-{!> ../../docs_src/python_types/tutorial011_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python
-{!> ../../docs_src/python_types/tutorial011.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial011_py310.py *}
/// info | Info
Python bietet auch die Möglichkeit, **zusätzliche <abbr title="Daten über die Daten, in diesem Fall Informationen über den Typ, z. B. eine Beschreibung.">Metadaten</abbr>** in Typhinweisen unterzubringen, mittels `Annotated`.
-//// tab | Python 3.9+
-
-In Python 3.9 ist `Annotated` ein Teil der Standardbibliothek, Sie können es von `typing` importieren.
+Seit Python 3.9 ist `Annotated` ein Teil der Standardbibliothek, Sie können es von `typing` importieren.
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial013_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-In Versionen niedriger als Python 3.9 importieren Sie `Annotated` von `typing_extensions`.
-
-Es wird bereits mit **FastAPI** installiert sein.
-
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial013.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial013_py39.py hl[1,4] *}
Python selbst macht nichts mit `Annotated`. Für Editoren und andere Tools ist der Typ immer noch `str`.
Importieren Sie zunächst `BackgroundTasks` und definieren Sie einen Parameter in Ihrer *Pfadoperation-Funktion* mit der Typdeklaration `BackgroundTasks`:
-{* ../../docs_src/background_tasks/tutorial001.py hl[1,13] *}
+{* ../../docs_src/background_tasks/tutorial001_py39.py hl[1,13] *}
**FastAPI** erstellt für Sie das Objekt vom Typ `BackgroundTasks` und übergibt es als diesen Parameter.
Und da der Schreibvorgang nicht `async` und `await` verwendet, definieren wir die Funktion mit normalem `def`:
-{* ../../docs_src/background_tasks/tutorial001.py hl[6:9] *}
+{* ../../docs_src/background_tasks/tutorial001_py39.py hl[6:9] *}
## Den Hintergrundtask hinzufügen { #add-the-background-task }
Übergeben Sie innerhalb Ihrer *Pfadoperation-Funktion* Ihre Taskfunktion mit der Methode `.add_task()` an das *Hintergrundtasks*-Objekt:
-{* ../../docs_src/background_tasks/tutorial001.py hl[14] *}
+{* ../../docs_src/background_tasks/tutorial001_py39.py hl[14] *}
`.add_task()` erhält als Argumente:
Aber Python erlaubt es, Listen mit inneren Typen, auch „Typ-Parameter“ genannt, zu deklarieren.
-### `List` von `typing` importieren { #import-typings-list }
-
-In Python 3.9 oder darüber können Sie einfach `list` verwenden, um diese Typannotationen zu deklarieren, wie wir unten sehen werden. 💡
-
-In Python-Versionen vor 3.9 (3.6 und darüber), müssen Sie zuerst `List` von Pythons Standardmodul `typing` importieren.
-
-{* ../../docs_src/body_nested_models/tutorial002.py hl[1] *}
-
### Eine `list` mit einem Typ-Parameter deklarieren { #declare-a-list-with-a-type-parameter }
-Um Typen wie `list`, `dict`, `tuple` mit inneren Typ-Parametern (inneren Typen) zu deklarieren:
-
-* Wenn Sie eine Python-Version kleiner als 3.9 verwenden, importieren Sie das Äquivalent zum entsprechenden Typ vom `typing`-Modul
-* Überreichen Sie den/die inneren Typ(en) von eckigen Klammern umschlossen, `[` und `]`, als „Typ-Parameter“
-
-In Python 3.9 wäre das:
+Um Typen zu deklarieren, die Typ-Parameter (innere Typen) haben, wie `list`, `dict`, `tuple`, übergeben Sie den/die inneren Typ(en) als „Typ-Parameter“ in eckigen Klammern: `[` und `]`
```Python
my_list: list[str]
```
-Und in Python-Versionen vor 3.9:
-
-```Python
-from typing import List
-
-my_list: List[str]
-```
-
Das ist alles Standard-Python-Syntax für Typdeklarationen.
Verwenden Sie dieselbe Standardsyntax für Modellattribute mit inneren Typen.
Wenn das äußerste Element des JSON-Bodys, das Sie erwarten, ein JSON-`array` (eine Python-`list`) ist, können Sie den Typ im Funktionsparameter deklarieren, mit der gleichen Syntax wie in Pydantic-Modellen:
-```Python
-images: List[Image]
-```
-
-oder in Python 3.9 und darüber:
-
```Python
images: list[Image]
```
FastAPI weiß, dass der Wert von `q` nicht erforderlich ist, aufgrund des definierten Defaultwertes `= None`.
-Das `str | None` (Python 3.10+) oder `Union` in `Union[str, None]` (Python 3.8+) wird von FastAPI nicht verwendet, um zu bestimmen, dass der Wert nicht erforderlich ist. FastAPI weiß, dass er nicht erforderlich ist, weil er einen Defaultwert von `= None` hat.
+Das `str | None` (Python 3.10+) oder `Union` in `Union[str, None]` (Python 3.9+) wird von FastAPI nicht verwendet, um zu bestimmen, dass der Wert nicht erforderlich ist. FastAPI weiß, dass er nicht erforderlich ist, weil er einen Defaultwert von `= None` hat.
Das Hinzufügen der Typannotationen ermöglicht jedoch Ihrem Editor, Ihnen eine bessere Unterstützung zu bieten und Fehler zu erkennen.
* Bestimmte HTTP-Methoden (`POST`, `PUT`) oder alle mit der Wildcard `"*"`.
* Bestimmte HTTP-Header oder alle mit der Wildcard `"*"`.
-{* ../../docs_src/cors/tutorial001.py hl[2,6:11,13:19] *}
+{* ../../docs_src/cors/tutorial001_py39.py hl[2,6:11,13:19] *}
Die von der `CORSMiddleware`-Implementierung verwendeten Defaultparameter sind standardmäßig restriktiv, daher müssen Sie bestimmte Origins, Methoden oder Header ausdrücklich aktivieren, damit Browser sie in einem Cross-Domain-Kontext verwenden dürfen.
Importieren und führen Sie `uvicorn` direkt in Ihrer FastAPI-Anwendung aus:
-{* ../../docs_src/debugging/tutorial001.py hl[1,15] *}
+{* ../../docs_src/debugging/tutorial001_py39.py hl[1,15] *}
### Über `__name__ == "__main__"` { #about-name-main }
Beachten Sie, wie wir `CommonQueryParams` im obigen Code zweimal schreiben:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
-//// tab | Python 3.8+ nicht annotiert
+//// tab | Python 3.9+ nicht annotiert
/// tip | Tipp
In diesem Fall hat das erste `CommonQueryParams` in:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, ...
////
-//// tab | Python 3.8+ nicht annotiert
+//// tab | Python 3.9+ nicht annotiert
/// tip | Tipp
Sie könnten tatsächlich einfach schreiben:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[Any, Depends(CommonQueryParams)]
////
-//// tab | Python 3.8+ nicht annotiert
+//// tab | Python 3.9+ nicht annotiert
/// tip | Tipp
Aber Sie sehen, dass wir hier etwas Codeduplizierung haben, indem wir `CommonQueryParams` zweimal schreiben:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
-//// tab | Python 3.8+ nicht annotiert
+//// tab | Python 3.9+ nicht annotiert
/// tip | Tipp
Anstatt zu schreiben:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
-//// tab | Python 3.8+ nicht annotiert
+//// tab | Python 3.9+ nicht annotiert
/// tip | Tipp
... schreiben Sie:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends()]
////
-//// tab | Python 3.8 nicht annotiert
+//// tab | Python 3.9+ nicht annotiert
/// tip | Tipp
Nur der Code vor und einschließlich der `yield`-Anweisung wird ausgeführt, bevor eine <abbr title="Response – Antwort: Daten, die der Server zum anfragenden Client zurücksendet">Response</abbr> erzeugt wird:
-{* ../../docs_src/dependencies/tutorial007.py hl[2:4] *}
+{* ../../docs_src/dependencies/tutorial007_py39.py hl[2:4] *}
Der ge`yield`ete Wert ist das, was in *Pfadoperationen* und andere Abhängigkeiten eingefügt wird:
-{* ../../docs_src/dependencies/tutorial007.py hl[4] *}
+{* ../../docs_src/dependencies/tutorial007_py39.py hl[4] *}
Der auf die `yield`-Anweisung folgende Code wird nach der Response ausgeführt:
-{* ../../docs_src/dependencies/tutorial007.py hl[5:6] *}
+{* ../../docs_src/dependencies/tutorial007_py39.py hl[5:6] *}
/// tip | Tipp
Auf die gleiche Weise können Sie `finally` verwenden, um sicherzustellen, dass die Exit-Schritte ausgeführt werden, unabhängig davon, ob eine Exception geworfen wurde oder nicht.
-{* ../../docs_src/dependencies/tutorial007.py hl[3,5] *}
+{* ../../docs_src/dependencies/tutorial007_py39.py hl[3,5] *}
## Unterabhängigkeiten mit `yield` { #sub-dependencies-with-yield }
Sie können solche auch innerhalb von **FastAPI**-Abhängigkeiten mit `yield` verwenden, indem Sie `with`- oder `async with`-Anweisungen innerhalb der Abhängigkeits-Funktion verwenden:
-{* ../../docs_src/dependencies/tutorial010.py hl[1:9,13] *}
+{* ../../docs_src/dependencies/tutorial010_py39.py hl[1:9,13] *}
/// tip | Tipp
In diesem Fall werden sie auf alle *Pfadoperationen* in der Anwendung angewendet:
-{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[16] *}
+{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[17] *}
Und alle Ideen aus dem Abschnitt über das [Hinzufügen von `dependencies` zu den *Pfadoperation-Dekoratoren*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} gelten weiterhin, aber in diesem Fall für alle *Pfadoperationen* in der App.
In einem fortgeschrittenen Szenario, bei dem Sie wissen, dass die Abhängigkeit bei jedem Schritt (möglicherweise mehrmals) in demselben Request aufgerufen werden muss, anstatt den zwischengespeicherten Wert zu verwenden, können Sie den Parameter `use_cache=False` festlegen, wenn Sie `Depends` verwenden:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python hl_lines="1"
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
////
-//// tab | Python 3.8+ nicht annotiert
+//// tab | Python 3.9+ nicht annotiert
/// tip | Tipp
Die einfachste FastAPI-Datei könnte wie folgt aussehen:
-{* ../../docs_src/first_steps/tutorial001.py *}
+{* ../../docs_src/first_steps/tutorial001_py39.py *}
Kopieren Sie das in eine Datei `main.py`.
### Schritt 1: `FastAPI` importieren { #step-1-import-fastapi }
-{* ../../docs_src/first_steps/tutorial001.py hl[1] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[1] *}
`FastAPI` ist eine Python-Klasse, die die gesamte Funktionalität für Ihre API bereitstellt.
### Schritt 2: Erzeugen einer `FastAPI`-„Instanz“ { #step-2-create-a-fastapi-instance }
-{* ../../docs_src/first_steps/tutorial001.py hl[3] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[3] *}
In diesem Beispiel ist die Variable `app` eine „Instanz“ der Klasse `FastAPI`.
#### Definieren eines *Pfadoperation-Dekorators* { #define-a-path-operation-decorator }
-{* ../../docs_src/first_steps/tutorial001.py hl[6] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[6] *}
Das `@app.get("/")` sagt **FastAPI**, dass die Funktion direkt darunter für die Bearbeitung von <abbr title="Request – Anfrage: Daten, die der Client zum Server sendet">Requests</abbr> zuständig ist, die an:
* **Operation**: ist `get`.
* **Funktion**: ist die Funktion direkt unter dem „Dekorator“ (unter `@app.get("/")`).
-{* ../../docs_src/first_steps/tutorial001.py hl[7] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[7] *}
Dies ist eine Python-Funktion.
Sie könnten sie auch als normale Funktion anstelle von `async def` definieren:
-{* ../../docs_src/first_steps/tutorial003.py hl[7] *}
+{* ../../docs_src/first_steps/tutorial003_py39.py hl[7] *}
/// note | Hinweis
### Schritt 5: den Inhalt zurückgeben { #step-5-return-the-content }
-{* ../../docs_src/first_steps/tutorial001.py hl[8] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[8] *}
Sie können ein `dict`, eine `list`, einzelne Werte wie `str`, `int`, usw. zurückgeben.
### `HTTPException` importieren { #import-httpexception }
-{* ../../docs_src/handling_errors/tutorial001.py hl[1] *}
+{* ../../docs_src/handling_errors/tutorial001_py39.py hl[1] *}
### Eine `HTTPException` in Ihrem Code auslösen { #raise-an-httpexception-in-your-code }
In diesem Beispiel lösen wir eine Exception mit einem Statuscode von `404` aus, wenn der Client einen Artikel mit einer nicht existierenden ID anfordert:
-{* ../../docs_src/handling_errors/tutorial001.py hl[11] *}
+{* ../../docs_src/handling_errors/tutorial001_py39.py hl[11] *}
### Die resultierende Response { #the-resulting-response }
Aber falls Sie es für ein fortgeschrittenes Szenario benötigen, können Sie benutzerdefinierte Header hinzufügen:
-{* ../../docs_src/handling_errors/tutorial002.py hl[14] *}
+{* ../../docs_src/handling_errors/tutorial002_py39.py hl[14] *}
## Benutzerdefinierte Exceptionhandler installieren { #install-custom-exception-handlers }
Sie könnten einen benutzerdefinierten Exceptionhandler mit `@app.exception_handler()` hinzufügen:
-{* ../../docs_src/handling_errors/tutorial003.py hl[5:7,13:18,24] *}
+{* ../../docs_src/handling_errors/tutorial003_py39.py hl[5:7,13:18,24] *}
Hier, wenn Sie `/unicorns/yolo` anfordern, wird die *Pfadoperation* eine `UnicornException` `raise`n.
Der Exceptionhandler erhält einen `Request` und die Exception.
-{* ../../docs_src/handling_errors/tutorial004.py hl[2,14:19] *}
+{* ../../docs_src/handling_errors/tutorial004_py39.py hl[2,14:19] *}
Wenn Sie nun zu `/items/foo` gehen, erhalten Sie anstelle des standardmäßigen JSON-Fehlers mit:
Zum Beispiel könnten Sie eine Klartext-Response statt JSON für diese Fehler zurückgeben wollen:
-{* ../../docs_src/handling_errors/tutorial004.py hl[3:4,9:11,25] *}
+{* ../../docs_src/handling_errors/tutorial004_py39.py hl[3:4,9:11,25] *}
/// note | Technische Details
Sie könnten diesen während der Entwicklung Ihrer Anwendung verwenden, um den Body zu loggen und zu debuggen, ihn an den Benutzer zurückzugeben usw.
-{* ../../docs_src/handling_errors/tutorial005.py hl[14] *}
+{* ../../docs_src/handling_errors/tutorial005_py39.py hl[14] *}
Versuchen Sie nun, einen ungültigen Artikel zu senden:
Wenn Sie die Exception zusammen mit den gleichen Default-Exceptionhandlern von **FastAPI** verwenden möchten, können Sie die Default-Exceptionhandler aus `fastapi.exception_handlers` importieren und wiederverwenden:
-{* ../../docs_src/handling_errors/tutorial006.py hl[2:5,15,21] *}
+{* ../../docs_src/handling_errors/tutorial006_py39.py hl[2:5,15,21] *}
In diesem Beispiel geben Sie nur den Fehler mit einer sehr ausdrucksstarken Nachricht aus, aber Sie verstehen das Prinzip. Sie können die Exception verwenden und dann einfach die Default-Exceptionhandler wiederverwenden.
Sie können diese wie folgt setzen:
-{* ../../docs_src/metadata/tutorial001.py hl[3:16, 19:32] *}
+{* ../../docs_src/metadata/tutorial001_py39.py hl[3:16, 19:32] *}
/// tip | Tipp
Zum Beispiel:
-{* ../../docs_src/metadata/tutorial001_1.py hl[31] *}
+{* ../../docs_src/metadata/tutorial001_1_py39.py hl[31] *}
## Metadaten für Tags { #metadata-for-tags }
Erstellen Sie Metadaten für Ihre Tags und übergeben Sie diese an den Parameter `openapi_tags`:
-{* ../../docs_src/metadata/tutorial004.py hl[3:16,18] *}
+{* ../../docs_src/metadata/tutorial004_py39.py hl[3:16,18] *}
Beachten Sie, dass Sie Markdown innerhalb der Beschreibungen verwenden können. Zum Beispiel wird „login“ in Fettschrift (**login**) und „fancy“ in Kursivschrift (_fancy_) angezeigt.
Verwenden Sie den Parameter `tags` mit Ihren *Pfadoperationen* (und `APIRouter`n), um diese verschiedenen Tags zuzuweisen:
-{* ../../docs_src/metadata/tutorial004.py hl[21,26] *}
+{* ../../docs_src/metadata/tutorial004_py39.py hl[21,26] *}
/// info | Info
Um beispielsweise festzulegen, dass es unter `/api/v1/openapi.json` bereitgestellt wird:
-{* ../../docs_src/metadata/tutorial002.py hl[3] *}
+{* ../../docs_src/metadata/tutorial002_py39.py hl[3] *}
Wenn Sie das OpenAPI-Schema vollständig deaktivieren möchten, können Sie `openapi_url=None` festlegen, wodurch auch die Dokumentationsbenutzeroberflächen deaktiviert werden, die es verwenden.
Um beispielsweise Swagger UI so einzustellen, dass sie unter `/documentation` bereitgestellt wird, und ReDoc zu deaktivieren:
-{* ../../docs_src/metadata/tutorial003.py hl[3] *}
+{* ../../docs_src/metadata/tutorial003_py39.py hl[3] *}
* Dann gibt es die von der entsprechenden *Pfadoperation* generierte `response` zurück.
* Sie können die `response` dann weiter modifizieren, bevor Sie sie zurückgeben.
-{* ../../docs_src/middleware/tutorial001.py hl[8:9,11,14] *}
+{* ../../docs_src/middleware/tutorial001_py39.py hl[8:9,11,14] *}
/// tip | Tipp
Sie könnten beispielsweise einen benutzerdefinierten Header `X-Process-Time` hinzufügen, der die Zeit in Sekunden enthält, die benötigt wurde, um den Request zu verarbeiten und eine Response zu generieren:
-{* ../../docs_src/middleware/tutorial001.py hl[10,12:13] *}
+{* ../../docs_src/middleware/tutorial001_py39.py hl[10,12:13] *}
/// tip | Tipp
**FastAPI** unterstützt das auf die gleiche Weise wie einfache Strings:
-{* ../../docs_src/path_operation_configuration/tutorial002b.py hl[1,8:10,13,18] *}
+{* ../../docs_src/path_operation_configuration/tutorial002b_py39.py hl[1,8:10,13,18] *}
## Zusammenfassung und Beschreibung { #summary-and-description }
Wenn Sie eine *Pfadoperation* als <abbr title="veraltet, obsolet: Es soll nicht mehr verwendet werden">deprecatet</abbr> kennzeichnen möchten, ohne sie zu entfernen, fügen Sie den Parameter `deprecated` hinzu:
-{* ../../docs_src/path_operation_configuration/tutorial006.py hl[16] *}
+{* ../../docs_src/path_operation_configuration/tutorial006_py39.py hl[16] *}
Sie wird in der interaktiven Dokumentation gut sichtbar als deprecatet markiert werden:
Sie können Ihre Funktion also so deklarieren:
-{* ../../docs_src/path_params_numeric_validations/tutorial002.py hl[7] *}
+{* ../../docs_src/path_params_numeric_validations/tutorial002_py39.py hl[7] *}
Aber bedenken Sie, dass Sie dieses Problem nicht haben, wenn Sie `Annotated` verwenden, da es nicht darauf ankommt, dass Sie keine Funktionsparameter-Defaultwerte für `Query()` oder `Path()` verwenden.
Python wird nichts mit diesem `*` machen, aber es wird wissen, dass alle folgenden Parameter als Schlüsselwortargumente (Schlüssel-Wert-Paare) verwendet werden sollen, auch bekannt als <abbr title="Von: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>. Selbst wenn diese keinen Defaultwert haben.
-{* ../../docs_src/path_params_numeric_validations/tutorial003.py hl[7] *}
+{* ../../docs_src/path_params_numeric_validations/tutorial003_py39.py hl[7] *}
### Besser mit `Annotated` { #better-with-annotated }
Sie können Pfad-„Parameter“ oder -„Variablen“ mit der gleichen Syntax deklarieren, welche in Python-<abbr title="Formatstring – Formatierter String: Der String enthält Ausdrücke, die mit geschweiften Klammern umschlossen sind. Solche Stellen werden durch den Wert des Ausdrucks ersetzt">Formatstrings</abbr> verwendet wird:
-{* ../../docs_src/path_params/tutorial001.py hl[6:7] *}
+{* ../../docs_src/path_params/tutorial001_py39.py hl[6:7] *}
Der Wert des Pfad-Parameters `item_id` wird Ihrer Funktion als das Argument `item_id` übergeben.
Sie können den Typ eines Pfad-Parameters in der Argumentliste der Funktion deklarieren, mit Standard-Python-Typannotationen:
-{* ../../docs_src/path_params/tutorial002.py hl[7] *}
+{* ../../docs_src/path_params/tutorial002_py39.py hl[7] *}
In diesem Fall wird `item_id` als `int` deklariert, also als Ganzzahl.
Weil *Pfadoperationen* in ihrer Reihenfolge ausgewertet werden, müssen Sie sicherstellen, dass der Pfad `/users/me` vor `/users/{user_id}` deklariert wurde:
-{* ../../docs_src/path_params/tutorial003.py hl[6,11] *}
+{* ../../docs_src/path_params/tutorial003_py39.py hl[6,11] *}
Ansonsten würde der Pfad für `/users/{user_id}` auch `/users/me` auswerten, und annehmen, dass ein Parameter `user_id` mit dem Wert `"me"` übergeben wurde.
Sie können eine Pfadoperation auch nicht erneut definieren:
-{* ../../docs_src/path_params/tutorial003b.py hl[6,11] *}
+{* ../../docs_src/path_params/tutorial003b_py39.py hl[6,11] *}
Die erste Definition wird immer verwendet werden, da ihr Pfad zuerst übereinstimmt.
Erstellen Sie dann Klassen-Attribute mit festgelegten Werten, welches die erlaubten Werte sein werden:
-{* ../../docs_src/path_params/tutorial005.py hl[1,6:9] *}
+{* ../../docs_src/path_params/tutorial005_py39.py hl[1,6:9] *}
-/// info | Info
-
-<a href="https://docs.python.org/3/library/enum.html" class="external-link" target="_blank">Enumerationen (oder Enums)</a> gibt es in Python seit Version 3.4.
-
-///
/// tip | Tipp
Dann erstellen Sie einen *Pfad-Parameter*, der als Typ die gerade erstellte Enum-Klasse hat (`ModelName`):
-{* ../../docs_src/path_params/tutorial005.py hl[16] *}
+{* ../../docs_src/path_params/tutorial005_py39.py hl[16] *}
### Die API-Dokumentation testen { #check-the-docs }
Sie können ihn mit einem Member Ihrer Enumeration `ModelName` vergleichen:
-{* ../../docs_src/path_params/tutorial005.py hl[17] *}
+{* ../../docs_src/path_params/tutorial005_py39.py hl[17] *}
#### *Enumerations-Wert* erhalten { #get-the-enumeration-value }
Den tatsächlichen Wert (in diesem Fall ein `str`) erhalten Sie via `model_name.value`, oder generell, `your_enum_member.value`:
-{* ../../docs_src/path_params/tutorial005.py hl[20] *}
+{* ../../docs_src/path_params/tutorial005_py39.py hl[20] *}
/// tip | Tipp
Diese werden zu ihren entsprechenden Werten konvertiert (in diesem Fall Strings), bevor sie zum Client übertragen werden:
-{* ../../docs_src/path_params/tutorial005.py hl[18,21,23] *}
+{* ../../docs_src/path_params/tutorial005_py39.py hl[18,21,23] *}
In Ihrem Client erhalten Sie eine JSON-Response, wie etwa:
Sie verwenden das also wie folgt:
-{* ../../docs_src/path_params/tutorial004.py hl[6] *}
+{* ../../docs_src/path_params/tutorial004_py39.py hl[6] *}
/// tip | Tipp
////
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
q: Union[str, None] = None
////
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
q: Annotated[Union[str, None]] = None
Wenn Sie in Ihrer Funktion andere Parameter deklarieren, die nicht Teil der Pfad-Parameter sind, dann werden diese automatisch als „Query“-Parameter interpretiert.
-{* ../../docs_src/query_params/tutorial001.py hl[9] *}
+{* ../../docs_src/query_params/tutorial001_py39.py hl[9] *}
Die <abbr title="Abfrage">Query</abbr> ist die Menge von Schlüssel-Wert-Paaren, die nach dem `?` in einer URL folgen und durch `&`-Zeichen getrennt sind.
Aber wenn Sie wollen, dass ein Query-Parameter erforderlich ist, vergeben Sie einfach keinen Defaultwert:
-{* ../../docs_src/query_params/tutorial005.py hl[6:7] *}
+{* ../../docs_src/query_params/tutorial005_py39.py hl[6:7] *}
Hier ist `needy` ein erforderlicher Query-Parameter vom Typ `str`.
Der häufigste Anwendungsfall ist, wenn Sie [eine Response direkt zurückgeben, wie es später im Handbuch für fortgeschrittene Benutzer erläutert wird](../advanced/response-directly.md){.internal-link target=_blank}.
-{* ../../docs_src/response_model/tutorial003_02.py hl[8,10:11] *}
+{* ../../docs_src/response_model/tutorial003_02_py39.py hl[8,10:11] *}
Dieser einfache Anwendungsfall wird automatisch von FastAPI gehandhabt, weil die Annotation des Rückgabetyps die Klasse (oder eine Unterklasse von) `Response` ist.
Sie können auch eine Unterklasse von `Response` in der Typannotation verwenden.
-{* ../../docs_src/response_model/tutorial003_03.py hl[8:9] *}
+{* ../../docs_src/response_model/tutorial003_03_py39.py hl[8:9] *}
Das wird ebenfalls funktionieren, weil `RedirectResponse` eine Unterklasse von `Response` ist, und FastAPI sich um diesen einfachen Anwendungsfall automatisch kümmert.
* `@app.delete()`
* usw.
-{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
+{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
/// note | Hinweis
Lassen Sie uns das vorherige Beispiel noch einmal anschauen:
-{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
+{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
`201` ist der Statuscode für „Created“ („Erzeugt“).
Sie können die Annehmlichkeit von Variablen aus `fastapi.status` nutzen.
-{* ../../docs_src/response_status_code/tutorial002.py hl[1,6] *}
+{* ../../docs_src/response_status_code/tutorial002_py39.py hl[1,6] *}
Diese sind nur eine Annehmlichkeit, sie enthalten dieselbe Zahl, aber so können Sie die Autovervollständigung Ihres Editors verwenden, um sie zu finden:
* Importieren Sie `StaticFiles`.
* „Mounten“ Sie eine `StaticFiles()`-Instanz in einem bestimmten Pfad.
-{* ../../docs_src/static_files/tutorial001.py hl[2,6] *}
+{* ../../docs_src/static_files/tutorial001_py39.py hl[2,6] *}
/// note | Technische Details
Schreiben Sie einfache `assert`-Anweisungen mit den Standard-Python-Ausdrücken, die Sie überprüfen müssen (wiederum, Standard-`pytest`).
-{* ../../docs_src/app_testing/tutorial001.py hl[2,12,15:18] *}
+{* ../../docs_src/app_testing/tutorial001_py39.py hl[2,12,15:18] *}
/// tip | Tipp
In der Datei `main.py` haben Sie Ihre **FastAPI**-Anwendung:
-{* ../../docs_src/app_testing/main.py *}
+{* ../../docs_src/app_testing/app_a_py39/main.py *}
### Testdatei { #testing-file }
Da sich diese Datei im selben Package befindet, können Sie relative Importe verwenden, um das Objekt `app` aus dem `main`-Modul (`main.py`) zu importieren:
-{* ../../docs_src/app_testing/test_main.py hl[3] *}
+{* ../../docs_src/app_testing/app_a_py39/test_main.py hl[3] *}
... und haben den Code für die Tests wie zuvor.
For example, to declare another response with a status code `404` and a Pydantic model `Message`, you can write:
-{* ../../docs_src/additional_responses/tutorial001.py hl[18,22] *}
+{* ../../docs_src/additional_responses/tutorial001_py39.py hl[18,22] *}
/// note
And a response with a status code `200` that uses your `response_model`, but includes a custom `example`:
-{* ../../docs_src/additional_responses/tutorial003.py hl[20:31] *}
+{* ../../docs_src/additional_responses/tutorial003_py39.py hl[20:31] *}
It will all be combined and included in your OpenAPI, and shown in the API docs:
The file `main.py` would have:
-{* ../../docs_src/async_tests/main.py *}
+{* ../../docs_src/async_tests/app_a_py39/main.py *}
The file `test_main.py` would have the tests for `main.py`, it could look like this now:
-{* ../../docs_src/async_tests/test_main.py *}
+{* ../../docs_src/async_tests/app_a_py39/test_main.py *}
## Run it { #run-it }
The marker `@pytest.mark.anyio` tells pytest that this test function should be called asynchronously:
-{* ../../docs_src/async_tests/test_main.py hl[7] *}
+{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[7] *}
/// tip
Then we can create an `AsyncClient` with the app, and send async requests to it, using `await`.
-{* ../../docs_src/async_tests/test_main.py hl[9:12] *}
+{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[9:12] *}
This is the equivalent to:
For example, let's say you define a *path operation* `/items/`:
-{* ../../docs_src/behind_a_proxy/tutorial001_01.py hl[6] *}
+{* ../../docs_src/behind_a_proxy/tutorial001_01_py39.py hl[6] *}
If the client tries to go to `/items`, by default, it would be redirected to `/items/`.
Even though all your code is written assuming there's just `/app`.
-{* ../../docs_src/behind_a_proxy/tutorial001.py hl[6] *}
+{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[6] *}
And the proxy would be **"stripping"** the **path prefix** on the fly before transmitting the request to the app server (probably Uvicorn via FastAPI CLI), keeping your application convinced that it is being served at `/app`, so that you don't have to update all your code to include the prefix `/api/v1`.
Here we are including it in the message just for demonstration purposes.
-{* ../../docs_src/behind_a_proxy/tutorial001.py hl[8] *}
+{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[8] *}
Then, if you start Uvicorn with:
Alternatively, if you don't have a way to provide a command line option like `--root-path` or equivalent, you can set the `root_path` parameter when creating your FastAPI app:
-{* ../../docs_src/behind_a_proxy/tutorial002.py hl[3] *}
+{* ../../docs_src/behind_a_proxy/tutorial002_py39.py hl[3] *}
Passing the `root_path` to `FastAPI` would be the equivalent of passing the `--root-path` command line option to Uvicorn or Hypercorn.
For example:
-{* ../../docs_src/behind_a_proxy/tutorial003.py hl[4:7] *}
+{* ../../docs_src/behind_a_proxy/tutorial003_py39.py hl[4:7] *}
Will generate an OpenAPI schema like:
If you don't want **FastAPI** to include an automatic server using the `root_path`, you can use the parameter `root_path_in_servers=False`:
-{* ../../docs_src/behind_a_proxy/tutorial004.py hl[9] *}
+{* ../../docs_src/behind_a_proxy/tutorial004_py39.py hl[9] *}
and then it won't include it in the OpenAPI schema.
But if you are certain that the content that you are returning is **serializable with JSON**, you can pass it directly to the response class and avoid the extra overhead that FastAPI would have by passing your return content through the `jsonable_encoder` before passing it to the response class.
-{* ../../docs_src/custom_response/tutorial001b.py hl[2,7] *}
+{* ../../docs_src/custom_response/tutorial001b_py39.py hl[2,7] *}
/// info
* Import `HTMLResponse`.
* Pass `HTMLResponse` as the parameter `response_class` of your *path operation decorator*.
-{* ../../docs_src/custom_response/tutorial002.py hl[2,7] *}
+{* ../../docs_src/custom_response/tutorial002_py39.py hl[2,7] *}
/// info
The same example from above, returning an `HTMLResponse`, could look like:
-{* ../../docs_src/custom_response/tutorial003.py hl[2,7,19] *}
+{* ../../docs_src/custom_response/tutorial003_py39.py hl[2,7,19] *}
/// warning
For example, it could be something like:
-{* ../../docs_src/custom_response/tutorial004.py hl[7,21,23] *}
+{* ../../docs_src/custom_response/tutorial004_py39.py hl[7,21,23] *}
In this example, the function `generate_html_response()` already generates and returns a `Response` instead of returning the HTML in a `str`.
FastAPI (actually Starlette) will automatically include a Content-Length header. It will also include a Content-Type header, based on the `media_type` and appending a charset for text types.
-{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
+{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
### `HTMLResponse` { #htmlresponse }
Takes some text or bytes and returns a plain text response.
-{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *}
+{* ../../docs_src/custom_response/tutorial005_py39.py hl[2,7,9] *}
### `JSONResponse` { #jsonresponse }
///
-{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *}
+{* ../../docs_src/custom_response/tutorial001_py39.py hl[2,7] *}
/// tip
You can return a `RedirectResponse` directly:
-{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *}
+{* ../../docs_src/custom_response/tutorial006_py39.py hl[2,9] *}
---
Or you can use it in the `response_class` parameter:
-{* ../../docs_src/custom_response/tutorial006b.py hl[2,7,9] *}
+{* ../../docs_src/custom_response/tutorial006b_py39.py hl[2,7,9] *}
If you do that, then you can return the URL directly from your *path operation* function.
You can also use the `status_code` parameter combined with the `response_class` parameter:
-{* ../../docs_src/custom_response/tutorial006c.py hl[2,7,9] *}
+{* ../../docs_src/custom_response/tutorial006c_py39.py hl[2,7,9] *}
### `StreamingResponse` { #streamingresponse }
Takes an async generator or a normal generator/iterator and streams the response body.
-{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *}
+{* ../../docs_src/custom_response/tutorial007_py39.py hl[2,14] *}
#### Using `StreamingResponse` with file-like objects { #using-streamingresponse-with-file-like-objects }
This includes many libraries to interact with cloud storage, video processing, and others.
-{* ../../docs_src/custom_response/tutorial008.py hl[2,10:12,14] *}
+{* ../../docs_src/custom_response/tutorial008_py39.py hl[2,10:12,14] *}
1. This is the generator function. It's a "generator function" because it contains `yield` statements inside.
2. By using a `with` block, we make sure that the file-like object is closed after the generator function is done. So, after it finishes sending the response.
File responses will include appropriate `Content-Length`, `Last-Modified` and `ETag` headers.
-{* ../../docs_src/custom_response/tutorial009.py hl[2,10] *}
+{* ../../docs_src/custom_response/tutorial009_py39.py hl[2,10] *}
You can also use the `response_class` parameter:
-{* ../../docs_src/custom_response/tutorial009b.py hl[2,8,10] *}
+{* ../../docs_src/custom_response/tutorial009b_py39.py hl[2,8,10] *}
In this case, you can return the file path directly from your *path operation* function.
You could create a `CustomORJSONResponse`. The main thing you have to do is create a `Response.render(content)` method that returns the content as `bytes`:
-{* ../../docs_src/custom_response/tutorial009c.py hl[9:14,17] *}
+{* ../../docs_src/custom_response/tutorial009c_py39.py hl[9:14,17] *}
Now instead of returning:
In the example below, **FastAPI** will use `ORJSONResponse` by default, in all *path operations*, instead of `JSONResponse`.
-{* ../../docs_src/custom_response/tutorial010.py hl[2,4] *}
+{* ../../docs_src/custom_response/tutorial010_py39.py hl[2,4] *}
/// tip
We create an async function `lifespan()` with `yield` like this:
-{* ../../docs_src/events/tutorial003.py hl[16,19] *}
+{* ../../docs_src/events/tutorial003_py39.py hl[16,19] *}
Here we are simulating the expensive *startup* operation of loading the model by putting the (fake) model function in the dictionary with machine learning models before the `yield`. This code will be executed **before** the application **starts taking requests**, during the *startup*.
The first thing to notice, is that we are defining an async function with `yield`. This is very similar to Dependencies with `yield`.
-{* ../../docs_src/events/tutorial003.py hl[14:19] *}
+{* ../../docs_src/events/tutorial003_py39.py hl[14:19] *}
The first part of the function, before the `yield`, will be executed **before** the application starts.
That converts the function into something called an "**async context manager**".
-{* ../../docs_src/events/tutorial003.py hl[1,13] *}
+{* ../../docs_src/events/tutorial003_py39.py hl[1,13] *}
A **context manager** in Python is something that you can use in a `with` statement, for example, `open()` can be used as a context manager:
The `lifespan` parameter of the `FastAPI` app takes an **async context manager**, so we can pass our new `lifespan` async context manager to it.
-{* ../../docs_src/events/tutorial003.py hl[22] *}
+{* ../../docs_src/events/tutorial003_py39.py hl[22] *}
## Alternative Events (deprecated) { #alternative-events-deprecated }
To add a function that should be run before the application starts, declare it with the event `"startup"`:
-{* ../../docs_src/events/tutorial001.py hl[8] *}
+{* ../../docs_src/events/tutorial001_py39.py hl[8] *}
In this case, the `startup` event handler function will initialize the items "database" (just a `dict`) with some values.
To add a function that should be run when the application is shutting down, declare it with the event `"shutdown"`:
-{* ../../docs_src/events/tutorial002.py hl[6] *}
+{* ../../docs_src/events/tutorial002_py39.py hl[6] *}
Here, the `shutdown` event handler function will write a text line `"Application shutdown"` to a file `log.txt`.
We could download the OpenAPI JSON to a file `openapi.json` and then we could **remove that prefixed tag** with a script like this:
-{* ../../docs_src/generate_clients/tutorial004.py *}
+{* ../../docs_src/generate_clients/tutorial004_py39.py *}
//// tab | Node.js
Any incoming request to `http` or `ws` will be redirected to the secure scheme instead.
-{* ../../docs_src/advanced_middleware/tutorial001.py hl[2,6] *}
+{* ../../docs_src/advanced_middleware/tutorial001_py39.py hl[2,6] *}
## `TrustedHostMiddleware` { #trustedhostmiddleware }
Enforces that all incoming requests have a correctly set `Host` header, in order to guard against HTTP Host Header attacks.
-{* ../../docs_src/advanced_middleware/tutorial002.py hl[2,6:8] *}
+{* ../../docs_src/advanced_middleware/tutorial002_py39.py hl[2,6:8] *}
The following arguments are supported:
The middleware will handle both standard and streaming responses.
-{* ../../docs_src/advanced_middleware/tutorial003.py hl[2,6] *}
+{* ../../docs_src/advanced_middleware/tutorial003_py39.py hl[2,6] *}
The following arguments are supported:
When you create a **FastAPI** application, there is a `webhooks` attribute that you can use to define *webhooks*, the same way you would define *path operations*, for example with `@app.webhooks.post()`.
-{* ../../docs_src/openapi_webhooks/tutorial001.py hl[9:13,36:53] *}
+{* ../../docs_src/openapi_webhooks/tutorial001_py39.py hl[9:13,36:53] *}
The webhooks that you define will end up in the **OpenAPI** schema and the automatic **docs UI**.
You would have to make sure that it is unique for each operation.
-{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *}
### Using the *path operation function* name as the operationId { #using-the-path-operation-function-name-as-the-operationid }
You should do it after adding all your *path operations*.
-{* ../../docs_src/path_operation_advanced_configuration/tutorial002.py hl[2, 12:21, 24] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py39.py hl[2, 12:21, 24] *}
/// tip
To exclude a *path operation* from the generated OpenAPI schema (and thus, from the automatic documentation systems), use the parameter `include_in_schema` and set it to `False`:
-{* ../../docs_src/path_operation_advanced_configuration/tutorial003.py hl[6] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py39.py hl[6] *}
## Advanced description from docstring { #advanced-description-from-docstring }
This `openapi_extra` can be helpful, for example, to declare [OpenAPI Extensions](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
-{* ../../docs_src/path_operation_advanced_configuration/tutorial005.py hl[6] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py39.py hl[6] *}
If you open the automatic API docs, your extension will show up at the bottom of the specific *path operation*.
You could do that with `openapi_extra`:
-{* ../../docs_src/path_operation_advanced_configuration/tutorial006.py hl[19:36, 39:40] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *}
In this example, we didn't declare any Pydantic model. In fact, the request body is not even <abbr title="converted from some plain format, like bytes, into Python objects">parsed</abbr> as JSON, it is read directly as `bytes`, and the function `magic_data_reader()` would be in charge of parsing it in some way.
And then you can set the `status_code` in that *temporal* response object.
-{* ../../docs_src/response_change_status_code/tutorial001.py hl[1,9,12] *}
+{* ../../docs_src/response_change_status_code/tutorial001_py39.py hl[1,9,12] *}
And then you can return any object you need, as you normally would (a `dict`, a database model, etc).
And then you can set cookies in that *temporal* response object.
-{* ../../docs_src/response_cookies/tutorial002.py hl[1, 8:9] *}
+{* ../../docs_src/response_cookies/tutorial002_py39.py hl[1, 8:9] *}
And then you can return any object you need, as you normally would (a `dict`, a database model, etc).
Then set Cookies in it, and then return it:
-{* ../../docs_src/response_cookies/tutorial001.py hl[10:12] *}
+{* ../../docs_src/response_cookies/tutorial001_py39.py hl[10:12] *}
/// tip
You could put your XML content in a string, put that in a `Response`, and return it:
-{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
+{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
## Notes { #notes }
And then you can set headers in that *temporal* response object.
-{* ../../docs_src/response_headers/tutorial002.py hl[1, 7:8] *}
+{* ../../docs_src/response_headers/tutorial002_py39.py hl[1, 7:8] *}
And then you can return any object you need, as you normally would (a `dict`, a database model, etc).
Create a response as described in [Return a Response Directly](response-directly.md){.internal-link target=_blank} and pass the headers as an additional parameter:
-{* ../../docs_src/response_headers/tutorial001.py hl[10:12] *}
+{* ../../docs_src/response_headers/tutorial001_py39.py hl[10:12] *}
/// note | Technical Details
//// tab | Pydantic v2
-{* ../../docs_src/settings/tutorial001.py hl[2,5:8,11] *}
+{* ../../docs_src/settings/tutorial001_py39.py hl[2,5:8,11] *}
////
///
-{* ../../docs_src/settings/tutorial001_pv1.py hl[2,5:8,11] *}
+{* ../../docs_src/settings/tutorial001_pv1_py39.py hl[2,5:8,11] *}
////
Then you can use the new `settings` object in your application:
-{* ../../docs_src/settings/tutorial001.py hl[18:20] *}
+{* ../../docs_src/settings/tutorial001_py39.py hl[18:20] *}
### Run the server { #run-the-server }
For example, you could have a file `config.py` with:
-{* ../../docs_src/settings/app01/config.py *}
+{* ../../docs_src/settings/app01_py39/config.py *}
And then use it in a file `main.py`:
-{* ../../docs_src/settings/app01/main.py hl[3,11:13] *}
+{* ../../docs_src/settings/app01_py39/main.py hl[3,11:13] *}
/// tip
First, create the main, top-level, **FastAPI** application, and its *path operations*:
-{* ../../docs_src/sub_applications/tutorial001.py hl[3, 6:8] *}
+{* ../../docs_src/sub_applications/tutorial001_py39.py hl[3, 6:8] *}
### Sub-application { #sub-application }
This sub-application is just another standard FastAPI application, but this is the one that will be "mounted":
-{* ../../docs_src/sub_applications/tutorial001.py hl[11, 14:16] *}
+{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 14:16] *}
### Mount the sub-application { #mount-the-sub-application }
In this case, it will be mounted at the path `/subapi`:
-{* ../../docs_src/sub_applications/tutorial001.py hl[11, 19] *}
+{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 19] *}
### Check the automatic API docs { #check-the-automatic-api-docs }
* Declare a `Request` parameter in the *path operation* that will return a template.
* Use the `templates` you created to render and return a `TemplateResponse`, pass the name of the template, the request object, and a "context" dictionary with key-value pairs to be used inside of the Jinja2 template.
-{* ../../docs_src/templates/tutorial001.py hl[4,11,15:18] *}
+{* ../../docs_src/templates/tutorial001_py39.py hl[4,11,15:18] *}
/// note
When you need `lifespan` to run in your tests, you can use the `TestClient` with a `with` statement:
-{* ../../docs_src/app_testing/tutorial004.py hl[9:15,18,27:28,30:32,41:43] *}
+{* ../../docs_src/app_testing/tutorial004_py39.py hl[9:15,18,27:28,30:32,41:43] *}
You can read more details about the ["Running lifespan in tests in the official Starlette documentation site."](https://www.starlette.dev/lifespan/#running-lifespan-in-tests)
For the deprecated `startup` and `shutdown` events, you can use the `TestClient` as follows:
-{* ../../docs_src/app_testing/tutorial003.py hl[9:12,20:24] *}
+{* ../../docs_src/app_testing/tutorial003_py39.py hl[9:12,20:24] *}
For this, you use the `TestClient` in a `with` statement, connecting to the WebSocket:
-{* ../../docs_src/app_testing/tutorial002.py hl[27:31] *}
+{* ../../docs_src/app_testing/tutorial002_py39.py hl[27:31] *}
/// note
For that you need to access the request directly.
-{* ../../docs_src/using_request_directly/tutorial001.py hl[1,7:8] *}
+{* ../../docs_src/using_request_directly/tutorial001_py39.py hl[1,7:8] *}
By declaring a *path operation function* parameter with the type being the `Request` **FastAPI** will know to pass the `Request` in that parameter.
But it's the simplest way to focus on the server-side of WebSockets and have a working example:
-{* ../../docs_src/websockets/tutorial001.py hl[2,6:38,41:43] *}
+{* ../../docs_src/websockets/tutorial001_py39.py hl[2,6:38,41:43] *}
## Create a `websocket` { #create-a-websocket }
In your **FastAPI** application, create a `websocket`:
-{* ../../docs_src/websockets/tutorial001.py hl[1,46:47] *}
+{* ../../docs_src/websockets/tutorial001_py39.py hl[1,46:47] *}
/// note | Technical Details
In your WebSocket route you can `await` for messages and send messages.
-{* ../../docs_src/websockets/tutorial001.py hl[48:52] *}
+{* ../../docs_src/websockets/tutorial001_py39.py hl[48:52] *}
You can receive and send binary, text, and JSON data.
And then mount that under a path.
-{* ../../docs_src/wsgi/tutorial001.py hl[2:3,3] *}
+{* ../../docs_src/wsgi/tutorial001_py39.py hl[2:3,3] *}
## Check it { #check-it }
For example:
-{* ../../docs_src/conditional_openapi/tutorial001.py hl[6,11] *}
+{* ../../docs_src/conditional_openapi/tutorial001_py39.py hl[6,11] *}
Here we declare the setting `openapi_url` with the same default of `"/openapi.json"`.
But you can disable it by setting `syntaxHighlight` to `False`:
-{* ../../docs_src/configure_swagger_ui/tutorial001.py hl[3] *}
+{* ../../docs_src/configure_swagger_ui/tutorial001_py39.py hl[3] *}
...and then Swagger UI won't show the syntax highlighting anymore:
The same way you could set the syntax highlighting theme with the key `"syntaxHighlight.theme"` (notice that it has a dot in the middle):
-{* ../../docs_src/configure_swagger_ui/tutorial002.py hl[3] *}
+{* ../../docs_src/configure_swagger_ui/tutorial002_py39.py hl[3] *}
That configuration would change the syntax highlighting color theme:
For example, to disable `deepLinking` you could pass these settings to `swagger_ui_parameters`:
-{* ../../docs_src/configure_swagger_ui/tutorial003.py hl[3] *}
+{* ../../docs_src/configure_swagger_ui/tutorial003_py39.py hl[3] *}
## Other Swagger UI Parameters { #other-swagger-ui-parameters }
To disable them, set their URLs to `None` when creating your `FastAPI` app:
-{* ../../docs_src/custom_docs_ui/tutorial001.py hl[8] *}
+{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[8] *}
### Include the custom docs { #include-the-custom-docs }
And similarly for ReDoc...
-{* ../../docs_src/custom_docs_ui/tutorial001.py hl[2:6,11:19,22:24,27:33] *}
+{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[2:6,11:19,22:24,27:33] *}
/// tip
Now, to be able to test that everything works, create a *path operation*:
-{* ../../docs_src/custom_docs_ui/tutorial001.py hl[36:38] *}
+{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[36:38] *}
### Test it { #test-it }
* Import `StaticFiles`.
* "Mount" a `StaticFiles()` instance in a specific path.
-{* ../../docs_src/custom_docs_ui/tutorial002.py hl[7,11] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[7,11] *}
### Test the static files { #test-the-static-files }
To disable them, set their URLs to `None` when creating your `FastAPI` app:
-{* ../../docs_src/custom_docs_ui/tutorial002.py hl[9] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[9] *}
### Include the custom docs for static files { #include-the-custom-docs-for-static-files }
And similarly for ReDoc...
-{* ../../docs_src/custom_docs_ui/tutorial002.py hl[2:6,14:22,25:27,30:36] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[2:6,14:22,25:27,30:36] *}
/// tip
Now, to be able to test that everything works, create a *path operation*:
-{* ../../docs_src/custom_docs_ui/tutorial002.py hl[39:41] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[39:41] *}
### Test Static Files UI { #test-static-files-ui }
First, write all your **FastAPI** application as normally:
-{* ../../docs_src/extending_openapi/tutorial001.py hl[1,4,7:9] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[1,4,7:9] *}
### Generate the OpenAPI schema { #generate-the-openapi-schema }
Then, use the same utility function to generate the OpenAPI schema, inside a `custom_openapi()` function:
-{* ../../docs_src/extending_openapi/tutorial001.py hl[2,15:21] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[2,15:21] *}
### Modify the OpenAPI schema { #modify-the-openapi-schema }
Now you can add the ReDoc extension, adding a custom `x-logo` to the `info` "object" in the OpenAPI schema:
-{* ../../docs_src/extending_openapi/tutorial001.py hl[22:24] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[22:24] *}
### Cache the OpenAPI schema { #cache-the-openapi-schema }
It will be generated only once, and then the same cached schema will be used for the next requests.
-{* ../../docs_src/extending_openapi/tutorial001.py hl[13:14,25:26] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[13:14,25:26] *}
### Override the method { #override-the-method }
Now you can replace the `.openapi()` method with your new function.
-{* ../../docs_src/extending_openapi/tutorial001.py hl[29] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[29] *}
### Check it { #check-it }
Here's a small preview of how you could integrate Strawberry with FastAPI:
-{* ../../docs_src/graphql/tutorial001.py hl[3,22,25] *}
+{* ../../docs_src/graphql/tutorial001_py39.py hl[3,22,25] *}
You can learn more about Strawberry in the <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry documentation</a>.
* If the PR is for a feature, it should have docs.
* Unless it's a feature we want to discourage, like support for a corner case that we don't want users to use.
* The docs should include a source example file, not write Python directly in Markdown.
-* If the source example(s) file can have different syntax for Python 3.8, 3.9, 3.10, there should be different versions of the file, and they should be shown in tabs in the docs.
+* If the source example(s) file can have different syntax for different Python versions, there should be different versions of the file, and they should be shown in tabs in the docs.
* There should be tests testing the source example.
* Before the PR is applied, the new tests should fail.
* After applying the PR, the new tests should pass.
Let's start with a simple example:
-{* ../../docs_src/python_types/tutorial001.py *}
+{* ../../docs_src/python_types/tutorial001_py39.py *}
Calling this program outputs:
* Converts the first letter of each one to upper case with `title()`.
* <abbr title="Puts them together, as one. With the contents of one after the other.">Concatenates</abbr> them with a space in the middle.
-{* ../../docs_src/python_types/tutorial001.py hl[2] *}
+{* ../../docs_src/python_types/tutorial001_py39.py hl[2] *}
### Edit it { #edit-it }
Those are the "type hints":
-{* ../../docs_src/python_types/tutorial002.py hl[1] *}
+{* ../../docs_src/python_types/tutorial002_py39.py hl[1] *}
That is not the same as declaring default values like would be with:
Check this function, it already has type hints:
-{* ../../docs_src/python_types/tutorial003.py hl[1] *}
+{* ../../docs_src/python_types/tutorial003_py39.py hl[1] *}
Because the editor knows the types of the variables, you don't only get completion, you also get error checks:
Now you know that you have to fix it, convert `age` to a string with `str(age)`:
-{* ../../docs_src/python_types/tutorial004.py hl[2] *}
+{* ../../docs_src/python_types/tutorial004_py39.py hl[2] *}
## Declaring types { #declaring-types }
* `bool`
* `bytes`
-{* ../../docs_src/python_types/tutorial005.py hl[1] *}
+{* ../../docs_src/python_types/tutorial005_py39.py hl[1] *}
### Generic types with type parameters { #generic-types-with-type-parameters }
For example, let's define a variable to be a `list` of `str`.
-//// tab | Python 3.9+
-
Declare the variable, with the same colon (`:`) syntax.
As the type, put `list`.
As the list is a type that contains some internal types, you put them in square brackets:
-```Python hl_lines="1"
-{!> ../../docs_src/python_types/tutorial006_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-From `typing`, import `List` (with a capital `L`):
-
-```Python hl_lines="1"
-{!> ../../docs_src/python_types/tutorial006.py!}
-```
-
-Declare the variable, with the same colon (`:`) syntax.
-
-As the type, put the `List` that you imported from `typing`.
-
-As the list is a type that contains some internal types, you put them in square brackets:
-
-```Python hl_lines="4"
-{!> ../../docs_src/python_types/tutorial006.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial006_py39.py hl[1] *}
/// info
Those internal types in the square brackets are called "type parameters".
-In this case, `str` is the type parameter passed to `List` (or `list` in Python 3.9 and above).
+In this case, `str` is the type parameter passed to `list`.
///
That means: "the variable `items` is a `list`, and each of the items in this list is a `str`".
-/// tip
-
-If you use Python 3.9 or above, you don't have to import `List` from `typing`, you can use the same regular `list` type instead.
-
-///
-
By doing that, your editor can provide support even while processing items from the list:
<img src="/img/python-types/image05.png">
You would do the same to declare `tuple`s and `set`s:
-//// tab | Python 3.9+
-
-```Python hl_lines="1"
-{!> ../../docs_src/python_types/tutorial007_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial007.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial007_py39.py hl[1] *}
This means:
The second type parameter is for the values of the `dict`:
-//// tab | Python 3.9+
-
-```Python hl_lines="1"
-{!> ../../docs_src/python_types/tutorial008_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial008.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial008_py39.py hl[1] *}
This means:
////
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial008b.py!}
+{!> ../../docs_src/python_types/tutorial008b_py39.py!}
```
////
In Python 3.6 and above (including Python 3.10) you can declare it by importing and using `Optional` from the `typing` module.
```Python hl_lines="1 4"
-{!../../docs_src/python_types/tutorial009.py!}
+{!../../docs_src/python_types/tutorial009_py39.py!}
```
Using `Optional[str]` instead of just `str` will let the editor help you detect errors where you could be assuming that a value is always a `str`, when it could actually be `None` too.
////
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial009.py!}
+{!> ../../docs_src/python_types/tutorial009_py39.py!}
```
////
-//// tab | Python 3.8+ alternative
+//// tab | Python 3.9+ alternative
```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial009b.py!}
+{!> ../../docs_src/python_types/tutorial009b_py39.py!}
```
////
As an example, let's take this function:
-{* ../../docs_src/python_types/tutorial009c.py hl[1,4] *}
+{* ../../docs_src/python_types/tutorial009c_py39.py hl[1,4] *}
The parameter `name` is defined as `Optional[str]`, but it is **not optional**, you cannot call the function without the parameter:
* `set`
* `dict`
-And the same as with Python 3.8, from the `typing` module:
+And the same as with previous Python versions, from the `typing` module:
* `Union`
-* `Optional` (the same as with Python 3.8)
+* `Optional`
* ...and others.
In Python 3.10, as an alternative to using the generics `Union` and `Optional`, you can use the <abbr title='also called "bitwise or operator", but that meaning is not relevant here'>vertical bar (`|`)</abbr> to declare unions of types, that's a lot better and simpler.
* `set`
* `dict`
-And the same as with Python 3.8, from the `typing` module:
+And generics from the `typing` module:
* `Union`
* `Optional`
////
-//// tab | Python 3.8+
-
-* `List`
-* `Tuple`
-* `Set`
-* `Dict`
-* `Union`
-* `Optional`
-* ...and others.
-
-////
-
### Classes as types { #classes-as-types }
You can also declare a class as the type of a variable.
Let's say you have a class `Person`, with a name:
-{* ../../docs_src/python_types/tutorial010.py hl[1:3] *}
+{* ../../docs_src/python_types/tutorial010_py39.py hl[1:3] *}
Then you can declare a variable to be of type `Person`:
-{* ../../docs_src/python_types/tutorial010.py hl[6] *}
+{* ../../docs_src/python_types/tutorial010_py39.py hl[6] *}
And then, again, you get all the editor support:
An example from the official Pydantic docs:
-//// tab | Python 3.10+
-
-```Python
-{!> ../../docs_src/python_types/tutorial011_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python
-{!> ../../docs_src/python_types/tutorial011_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python
-{!> ../../docs_src/python_types/tutorial011.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial011_py310.py *}
/// info
Python also has a feature that allows putting **additional <abbr title="Data about the data, in this case, information about the type, e.g. a description.">metadata</abbr>** in these type hints using `Annotated`.
-//// tab | Python 3.9+
-
-In Python 3.9, `Annotated` is part of the standard library, so you can import it from `typing`.
+Since Python 3.9, `Annotated` is a part of the standard library, so you can import it from `typing`.
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial013_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-In versions below Python 3.9, you import `Annotated` from `typing_extensions`.
-
-It will already be installed with **FastAPI**.
-
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial013.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial013_py39.py hl[1,4] *}
Python itself doesn't do anything with this `Annotated`. And for editors and other tools, the type is still `str`.
First, import `BackgroundTasks` and define a parameter in your *path operation function* with a type declaration of `BackgroundTasks`:
-{* ../../docs_src/background_tasks/tutorial001.py hl[1,13] *}
+{* ../../docs_src/background_tasks/tutorial001_py39.py hl[1,13] *}
**FastAPI** will create the object of type `BackgroundTasks` for you and pass it as that parameter.
And as the write operation doesn't use `async` and `await`, we define the function with normal `def`:
-{* ../../docs_src/background_tasks/tutorial001.py hl[6:9] *}
+{* ../../docs_src/background_tasks/tutorial001_py39.py hl[6:9] *}
## Add the background task { #add-the-background-task }
Inside of your *path operation function*, pass your task function to the *background tasks* object with the method `.add_task()`:
-{* ../../docs_src/background_tasks/tutorial001.py hl[14] *}
+{* ../../docs_src/background_tasks/tutorial001_py39.py hl[14] *}
`.add_task()` receives as arguments:
But Python has a specific way to declare lists with internal types, or "type parameters":
-### Import typing's `List` { #import-typings-list }
-
-In Python 3.9 and above you can use the standard `list` to declare these type annotations as we'll see below. 💡
-
-But in Python versions before 3.9 (3.6 and above), you first need to import `List` from standard Python's `typing` module:
-
-{* ../../docs_src/body_nested_models/tutorial002.py hl[1] *}
-
### Declare a `list` with a type parameter { #declare-a-list-with-a-type-parameter }
-To declare types that have type parameters (internal types), like `list`, `dict`, `tuple`:
-
-* If you are in a Python version lower than 3.9, import their equivalent version from the `typing` module
-* Pass the internal type(s) as "type parameters" using square brackets: `[` and `]`
-
-In Python 3.9 it would be:
+To declare types that have type parameters (internal types), like `list`, `dict`, `tuple`,
+pass the internal type(s) as "type parameters" using square brackets: `[` and `]`
```Python
my_list: list[str]
```
-In versions of Python before 3.9, it would be:
-
-```Python
-from typing import List
-
-my_list: List[str]
-```
-
That's all standard Python syntax for type declarations.
Use that same standard syntax for model attributes with internal types.
If the top level value of the JSON body you expect is a JSON `array` (a Python `list`), you can declare the type in the parameter of the function, the same as in Pydantic models:
-```Python
-images: List[Image]
-```
-
-or in Python 3.9 and above:
-
```Python
images: list[Image]
```
FastAPI will know that the value of `q` is not required because of the default value `= None`.
-The `str | None` (Python 3.10+) or `Union` in `Union[str, None]` (Python 3.8+) is not used by FastAPI to determine that the value is not required, it will know it's not required because it has a default value of `= None`.
+The `str | None` (Python 3.10+) or `Union` in `Union[str, None]` (Python 3.9+) is not used by FastAPI to determine that the value is not required, it will know it's not required because it has a default value of `= None`.
But adding the type annotations will allow your editor to give you better support and detect errors.
* Specific HTTP methods (`POST`, `PUT`) or all of them with the wildcard `"*"`.
* Specific HTTP headers or all of them with the wildcard `"*"`.
-{* ../../docs_src/cors/tutorial001.py hl[2,6:11,13:19] *}
+{* ../../docs_src/cors/tutorial001_py39.py hl[2,6:11,13:19] *}
The default parameters used by the `CORSMiddleware` implementation are restrictive by default, so you'll need to explicitly enable particular origins, methods, or headers, in order for browsers to be permitted to use them in a Cross-Domain context.
In your FastAPI application, import and run `uvicorn` directly:
-{* ../../docs_src/debugging/tutorial001.py hl[1,15] *}
+{* ../../docs_src/debugging/tutorial001_py39.py hl[1,15] *}
### About `__name__ == "__main__"` { #about-name-main }
Notice how we write `CommonQueryParams` twice in the above code:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
-//// tab | Python 3.8+ non-Annotated
+//// tab | Python 3.9+ non-Annotated
/// tip
In this case, the first `CommonQueryParams`, in:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, ...
////
-//// tab | Python 3.8+ non-Annotated
+//// tab | Python 3.9+ non-Annotated
/// tip
You could actually write just:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[Any, Depends(CommonQueryParams)]
////
-//// tab | Python 3.8+ non-Annotated
+//// tab | Python 3.9+ non-Annotated
/// tip
But you see that we are having some code repetition here, writing `CommonQueryParams` twice:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
-//// tab | Python 3.8+ non-Annotated
+//// tab | Python 3.9+ non-Annotated
/// tip
Instead of writing:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
-//// tab | Python 3.8+ non-Annotated
+//// tab | Python 3.9+ non-Annotated
/// tip
...you write:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends()]
////
-//// tab | Python 3.8 non-Annotated
+//// tab | Python 3.9+ non-Annotated
/// tip
Only the code prior to and including the `yield` statement is executed before creating a response:
-{* ../../docs_src/dependencies/tutorial007.py hl[2:4] *}
+{* ../../docs_src/dependencies/tutorial007_py39.py hl[2:4] *}
The yielded value is what is injected into *path operations* and other dependencies:
-{* ../../docs_src/dependencies/tutorial007.py hl[4] *}
+{* ../../docs_src/dependencies/tutorial007_py39.py hl[4] *}
The code following the `yield` statement is executed after the response:
-{* ../../docs_src/dependencies/tutorial007.py hl[5:6] *}
+{* ../../docs_src/dependencies/tutorial007_py39.py hl[5:6] *}
/// tip
In the same way, you can use `finally` to make sure the exit steps are executed, no matter if there was an exception or not.
-{* ../../docs_src/dependencies/tutorial007.py hl[3,5] *}
+{* ../../docs_src/dependencies/tutorial007_py39.py hl[3,5] *}
## Sub-dependencies with `yield` { #sub-dependencies-with-yield }
You can also use them inside of **FastAPI** dependencies with `yield` by using
`with` or `async with` statements inside of the dependency function:
-{* ../../docs_src/dependencies/tutorial010.py hl[1:9,13] *}
+{* ../../docs_src/dependencies/tutorial010_py39.py hl[1:9,13] *}
/// tip
In that case, they will be applied to all the *path operations* in the application:
-{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[16] *}
+{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[17] *}
And all the ideas in the section about [adding `dependencies` to the *path operation decorators*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} still apply, but in this case, to all of the *path operations* in the app.
In an advanced scenario where you know you need the dependency to be called at every step (possibly multiple times) in the same request instead of using the "cached" value, you can set the parameter `use_cache=False` when using `Depends`:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python hl_lines="1"
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
////
-//// tab | Python 3.8+ non-Annotated
+//// tab | Python 3.9+ non-Annotated
/// tip
The simplest FastAPI file could look like this:
-{* ../../docs_src/first_steps/tutorial001.py *}
+{* ../../docs_src/first_steps/tutorial001_py39.py *}
Copy that to a file `main.py`.
### Step 1: import `FastAPI` { #step-1-import-fastapi }
-{* ../../docs_src/first_steps/tutorial001.py hl[1] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[1] *}
`FastAPI` is a Python class that provides all the functionality for your API.
### Step 2: create a `FastAPI` "instance" { #step-2-create-a-fastapi-instance }
-{* ../../docs_src/first_steps/tutorial001.py hl[3] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[3] *}
Here the `app` variable will be an "instance" of the class `FastAPI`.
#### Define a *path operation decorator* { #define-a-path-operation-decorator }
-{* ../../docs_src/first_steps/tutorial001.py hl[6] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[6] *}
The `@app.get("/")` tells **FastAPI** that the function right below is in charge of handling requests that go to:
* **operation**: is `get`.
* **function**: is the function below the "decorator" (below `@app.get("/")`).
-{* ../../docs_src/first_steps/tutorial001.py hl[7] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[7] *}
This is a Python function.
You could also define it as a normal function instead of `async def`:
-{* ../../docs_src/first_steps/tutorial003.py hl[7] *}
+{* ../../docs_src/first_steps/tutorial003_py39.py hl[7] *}
/// note
### Step 5: return the content { #step-5-return-the-content }
-{* ../../docs_src/first_steps/tutorial001.py hl[8] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[8] *}
You can return a `dict`, `list`, singular values as `str`, `int`, etc.
### Import `HTTPException` { #import-httpexception }
-{* ../../docs_src/handling_errors/tutorial001.py hl[1] *}
+{* ../../docs_src/handling_errors/tutorial001_py39.py hl[1] *}
### Raise an `HTTPException` in your code { #raise-an-httpexception-in-your-code }
In this example, when the client requests an item by an ID that doesn't exist, raise an exception with a status code of `404`:
-{* ../../docs_src/handling_errors/tutorial001.py hl[11] *}
+{* ../../docs_src/handling_errors/tutorial001_py39.py hl[11] *}
### The resulting response { #the-resulting-response }
But in case you needed it for an advanced scenario, you can add custom headers:
-{* ../../docs_src/handling_errors/tutorial002.py hl[14] *}
+{* ../../docs_src/handling_errors/tutorial002_py39.py hl[14] *}
## Install custom exception handlers { #install-custom-exception-handlers }
You could add a custom exception handler with `@app.exception_handler()`:
-{* ../../docs_src/handling_errors/tutorial003.py hl[5:7,13:18,24] *}
+{* ../../docs_src/handling_errors/tutorial003_py39.py hl[5:7,13:18,24] *}
Here, if you request `/unicorns/yolo`, the *path operation* will `raise` a `UnicornException`.
The exception handler will receive a `Request` and the exception.
-{* ../../docs_src/handling_errors/tutorial004.py hl[2,14:19] *}
+{* ../../docs_src/handling_errors/tutorial004_py39.py hl[2,14:19] *}
Now, if you go to `/items/foo`, instead of getting the default JSON error with:
For example, you could want to return a plain text response instead of JSON for these errors:
-{* ../../docs_src/handling_errors/tutorial004.py hl[3:4,9:11,25] *}
+{* ../../docs_src/handling_errors/tutorial004_py39.py hl[3:4,9:11,25] *}
/// note | Technical Details
You could use it while developing your app to log the body and debug it, return it to the user, etc.
-{* ../../docs_src/handling_errors/tutorial005.py hl[14] *}
+{* ../../docs_src/handling_errors/tutorial005_py39.py hl[14] *}
Now try sending an invalid item like:
If you want to use the exception along with the same default exception handlers from **FastAPI**, you can import and reuse the default exception handlers from `fastapi.exception_handlers`:
-{* ../../docs_src/handling_errors/tutorial006.py hl[2:5,15,21] *}
+{* ../../docs_src/handling_errors/tutorial006_py39.py hl[2:5,15,21] *}
In this example you are just printing the error with a very expressive message, but you get the idea. You can use the exception and then just reuse the default exception handlers.
You can set them as follows:
-{* ../../docs_src/metadata/tutorial001.py hl[3:16, 19:32] *}
+{* ../../docs_src/metadata/tutorial001_py39.py hl[3:16, 19:32] *}
/// tip
For example:
-{* ../../docs_src/metadata/tutorial001_1.py hl[31] *}
+{* ../../docs_src/metadata/tutorial001_1_py39.py hl[31] *}
## Metadata for tags { #metadata-for-tags }
Create metadata for your tags and pass it to the `openapi_tags` parameter:
-{* ../../docs_src/metadata/tutorial004.py hl[3:16,18] *}
+{* ../../docs_src/metadata/tutorial004_py39.py hl[3:16,18] *}
Notice that you can use Markdown inside of the descriptions, for example "login" will be shown in bold (**login**) and "fancy" will be shown in italics (_fancy_).
Use the `tags` parameter with your *path operations* (and `APIRouter`s) to assign them to different tags:
-{* ../../docs_src/metadata/tutorial004.py hl[21,26] *}
+{* ../../docs_src/metadata/tutorial004_py39.py hl[21,26] *}
/// info
For example, to set it to be served at `/api/v1/openapi.json`:
-{* ../../docs_src/metadata/tutorial002.py hl[3] *}
+{* ../../docs_src/metadata/tutorial002_py39.py hl[3] *}
If you want to disable the OpenAPI schema completely you can set `openapi_url=None`, that will also disable the documentation user interfaces that use it.
For example, to set Swagger UI to be served at `/documentation` and disable ReDoc:
-{* ../../docs_src/metadata/tutorial003.py hl[3] *}
+{* ../../docs_src/metadata/tutorial003_py39.py hl[3] *}
* Then it returns the `response` generated by the corresponding *path operation*.
* You can then further modify the `response` before returning it.
-{* ../../docs_src/middleware/tutorial001.py hl[8:9,11,14] *}
+{* ../../docs_src/middleware/tutorial001_py39.py hl[8:9,11,14] *}
/// tip
For example, you could add a custom header `X-Process-Time` containing the time in seconds that it took to process the request and generate a response:
-{* ../../docs_src/middleware/tutorial001.py hl[10,12:13] *}
+{* ../../docs_src/middleware/tutorial001_py39.py hl[10,12:13] *}
/// tip
**FastAPI** supports that the same way as with plain strings:
-{* ../../docs_src/path_operation_configuration/tutorial002b.py hl[1,8:10,13,18] *}
+{* ../../docs_src/path_operation_configuration/tutorial002b_py39.py hl[1,8:10,13,18] *}
## Summary and description { #summary-and-description }
If you need to mark a *path operation* as <abbr title="obsolete, recommended not to use it">deprecated</abbr>, but without removing it, pass the parameter `deprecated`:
-{* ../../docs_src/path_operation_configuration/tutorial006.py hl[16] *}
+{* ../../docs_src/path_operation_configuration/tutorial006_py39.py hl[16] *}
It will be clearly marked as deprecated in the interactive docs:
So, you can declare your function as:
-{* ../../docs_src/path_params_numeric_validations/tutorial002.py hl[7] *}
+{* ../../docs_src/path_params_numeric_validations/tutorial002_py39.py hl[7] *}
But keep in mind that if you use `Annotated`, you won't have this problem, it won't matter as you're not using the function parameter default values for `Query()` or `Path()`.
Python won't do anything with that `*`, but it will know that all the following parameters should be called as keyword arguments (key-value pairs), also known as <abbr title="From: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>. Even if they don't have a default value.
-{* ../../docs_src/path_params_numeric_validations/tutorial003.py hl[7] *}
+{* ../../docs_src/path_params_numeric_validations/tutorial003_py39.py hl[7] *}
### Better with `Annotated` { #better-with-annotated }
You can declare path "parameters" or "variables" with the same syntax used by Python format strings:
-{* ../../docs_src/path_params/tutorial001.py hl[6:7] *}
+{* ../../docs_src/path_params/tutorial001_py39.py hl[6:7] *}
The value of the path parameter `item_id` will be passed to your function as the argument `item_id`.
You can declare the type of a path parameter in the function, using standard Python type annotations:
-{* ../../docs_src/path_params/tutorial002.py hl[7] *}
+{* ../../docs_src/path_params/tutorial002_py39.py hl[7] *}
In this case, `item_id` is declared to be an `int`.
Because *path operations* are evaluated in order, you need to make sure that the path for `/users/me` is declared before the one for `/users/{user_id}`:
-{* ../../docs_src/path_params/tutorial003.py hl[6,11] *}
+{* ../../docs_src/path_params/tutorial003_py39.py hl[6,11] *}
Otherwise, the path for `/users/{user_id}` would match also for `/users/me`, "thinking" that it's receiving a parameter `user_id` with a value of `"me"`.
Similarly, you cannot redefine a path operation:
-{* ../../docs_src/path_params/tutorial003b.py hl[6,11] *}
+{* ../../docs_src/path_params/tutorial003b_py39.py hl[6,11] *}
The first one will always be used since the path matches first.
Then create class attributes with fixed values, which will be the available valid values:
-{* ../../docs_src/path_params/tutorial005.py hl[1,6:9] *}
-
-/// info
-
-<a href="https://docs.python.org/3/library/enum.html" class="external-link" target="_blank">Enumerations (or enums) are available in Python</a> since version 3.4.
-
-///
+{* ../../docs_src/path_params/tutorial005_py39.py hl[1,6:9] *}
/// tip
Then create a *path parameter* with a type annotation using the enum class you created (`ModelName`):
-{* ../../docs_src/path_params/tutorial005.py hl[16] *}
+{* ../../docs_src/path_params/tutorial005_py39.py hl[16] *}
### Check the docs { #check-the-docs }
You can compare it with the *enumeration member* in your created enum `ModelName`:
-{* ../../docs_src/path_params/tutorial005.py hl[17] *}
+{* ../../docs_src/path_params/tutorial005_py39.py hl[17] *}
#### Get the *enumeration value* { #get-the-enumeration-value }
You can get the actual value (a `str` in this case) using `model_name.value`, or in general, `your_enum_member.value`:
-{* ../../docs_src/path_params/tutorial005.py hl[20] *}
+{* ../../docs_src/path_params/tutorial005_py39.py hl[20] *}
/// tip
They will be converted to their corresponding values (strings in this case) before returning them to the client:
-{* ../../docs_src/path_params/tutorial005.py hl[18,21,23] *}
+{* ../../docs_src/path_params/tutorial005_py39.py hl[18,21,23] *}
In your client you will get a JSON response like:
So, you can use it with:
-{* ../../docs_src/path_params/tutorial004.py hl[6] *}
+{* ../../docs_src/path_params/tutorial004_py39.py hl[6] *}
/// tip
////
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
q: Union[str, None] = None
////
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
q: Annotated[Union[str, None]] = None
When you declare other function parameters that are not part of the path parameters, they are automatically interpreted as "query" parameters.
-{* ../../docs_src/query_params/tutorial001.py hl[9] *}
+{* ../../docs_src/query_params/tutorial001_py39.py hl[9] *}
The query is the set of key-value pairs that go after the `?` in a URL, separated by `&` characters.
But when you want to make a query parameter required, you can just not declare any default value:
-{* ../../docs_src/query_params/tutorial005.py hl[6:7] *}
+{* ../../docs_src/query_params/tutorial005_py39.py hl[6:7] *}
Here the query parameter `needy` is a required query parameter of type `str`.
The most common case would be [returning a Response directly as explained later in the advanced docs](../advanced/response-directly.md){.internal-link target=_blank}.
-{* ../../docs_src/response_model/tutorial003_02.py hl[8,10:11] *}
+{* ../../docs_src/response_model/tutorial003_02_py39.py hl[8,10:11] *}
This simple case is handled automatically by FastAPI because the return type annotation is the class (or a subclass of) `Response`.
You can also use a subclass of `Response` in the type annotation:
-{* ../../docs_src/response_model/tutorial003_03.py hl[8:9] *}
+{* ../../docs_src/response_model/tutorial003_03_py39.py hl[8:9] *}
This will also work because `RedirectResponse` is a subclass of `Response`, and FastAPI will automatically handle this simple case.
* `@app.delete()`
* etc.
-{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
+{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
/// note
Let's see the previous example again:
-{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
+{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
`201` is the status code for "Created".
You can use the convenience variables from `fastapi.status`.
-{* ../../docs_src/response_status_code/tutorial002.py hl[1,6] *}
+{* ../../docs_src/response_status_code/tutorial002_py39.py hl[1,6] *}
They are just a convenience, they hold the same number, but that way you can use the editor's autocomplete to find them:
* Import `StaticFiles`.
* "Mount" a `StaticFiles()` instance in a specific path.
-{* ../../docs_src/static_files/tutorial001.py hl[2,6] *}
+{* ../../docs_src/static_files/tutorial001_py39.py hl[2,6] *}
/// note | Technical Details
Write simple `assert` statements with the standard Python expressions that you need to check (again, standard `pytest`).
-{* ../../docs_src/app_testing/tutorial001.py hl[2,12,15:18] *}
+{* ../../docs_src/app_testing/tutorial001_py39.py hl[2,12,15:18] *}
/// tip
In the file `main.py` you have your **FastAPI** app:
-{* ../../docs_src/app_testing/main.py *}
+{* ../../docs_src/app_testing/app_a_py39/main.py *}
### Testing file { #testing-file }
Because this file is in the same package, you can use relative imports to import the object `app` from the `main` module (`main.py`):
-{* ../../docs_src/app_testing/test_main.py hl[3] *}
+{* ../../docs_src/app_testing/app_a_py39/test_main.py hl[3] *}
...and have the code for the tests just like before.
Por ejemplo, para declarar otro response con un código de estado `404` y un modelo Pydantic `Message`, puedes escribir:
-{* ../../docs_src/additional_responses/tutorial001.py hl[18,22] *}
+{* ../../docs_src/additional_responses/tutorial001_py39.py hl[18,22] *}
/// note | Nota
Y un response con un código de estado `200` que usa tu `response_model`, pero incluye un `example` personalizado:
-{* ../../docs_src/additional_responses/tutorial003.py hl[20:31] *}
+{* ../../docs_src/additional_responses/tutorial003_py39.py hl[20:31] *}
Todo se combinará e incluirá en tu OpenAPI, y se mostrará en la documentación de la API:
El archivo `main.py` tendría:
-{* ../../docs_src/async_tests/main.py *}
+{* ../../docs_src/async_tests/app_a_py39/main.py *}
El archivo `test_main.py` tendría los tests para `main.py`, podría verse así ahora:
-{* ../../docs_src/async_tests/test_main.py *}
+{* ../../docs_src/async_tests/app_a_py39/test_main.py *}
## Ejecútalo { #run-it }
El marcador `@pytest.mark.anyio` le dice a pytest que esta función de test debe ser llamada asíncronamente:
-{* ../../docs_src/async_tests/test_main.py hl[7] *}
+{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[7] *}
/// tip | Consejo
Luego podemos crear un `AsyncClient` con la app y enviar requests asíncronos a ella, usando `await`.
-{* ../../docs_src/async_tests/test_main.py hl[9:12] *}
+{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[9:12] *}
Esto es equivalente a:
Por ejemplo, digamos que defines una *path operation* `/items/`:
-{* ../../docs_src/behind_a_proxy/tutorial001_01.py hl[6] *}
+{* ../../docs_src/behind_a_proxy/tutorial001_01_py39.py hl[6] *}
Si el cliente intenta ir a `/items`, por defecto, sería redirigido a `/items/`.
Aunque todo tu código esté escrito asumiendo que solo existe `/app`.
-{* ../../docs_src/behind_a_proxy/tutorial001.py hl[6] *}
+{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[6] *}
Y el proxy estaría **"eliminando"** el **prefijo del path** sobre la marcha antes de transmitir el request al servidor de aplicaciones (probablemente Uvicorn a través de FastAPI CLI), manteniendo a tu aplicación convencida de que está siendo servida en `/app`, así que no tienes que actualizar todo tu código para incluir el prefijo `/api/v1`.
Aquí lo estamos incluyendo en el mensaje solo con fines de demostración.
-{* ../../docs_src/behind_a_proxy/tutorial001.py hl[8] *}
+{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[8] *}
Luego, si inicias Uvicorn con:
Alternativamente, si no tienes una forma de proporcionar una opción de línea de comandos como `--root-path` o su equivalente, puedes configurar el parámetro `root_path` al crear tu app de FastAPI:
-{* ../../docs_src/behind_a_proxy/tutorial002.py hl[3] *}
+{* ../../docs_src/behind_a_proxy/tutorial002_py39.py hl[3] *}
Pasar el `root_path` a `FastAPI` sería el equivalente a pasar la opción de línea de comandos `--root-path` a Uvicorn o Hypercorn.
Por ejemplo:
-{* ../../docs_src/behind_a_proxy/tutorial003.py hl[4:7] *}
+{* ../../docs_src/behind_a_proxy/tutorial003_py39.py hl[4:7] *}
Generará un esquema de OpenAPI como:
Si no quieres que **FastAPI** incluya un server automático usando el `root_path`, puedes usar el parámetro `root_path_in_servers=False`:
-{* ../../docs_src/behind_a_proxy/tutorial004.py hl[9] *}
+{* ../../docs_src/behind_a_proxy/tutorial004_py39.py hl[9] *}
y entonces no lo incluirá en el esquema de OpenAPI.
Pero si estás seguro de que el contenido que estás devolviendo es **serializable con JSON**, puedes pasarlo directamente a la clase de response y evitar la sobrecarga extra que FastAPI tendría al pasar tu contenido de retorno a través de `jsonable_encoder` antes de pasarlo a la clase de response.
-{* ../../docs_src/custom_response/tutorial001b.py hl[2,7] *}
+{* ../../docs_src/custom_response/tutorial001b_py39.py hl[2,7] *}
/// info | Información
* Importa `HTMLResponse`.
* Pasa `HTMLResponse` como parámetro `response_class` de tu *path operation decorator*.
-{* ../../docs_src/custom_response/tutorial002.py hl[2,7] *}
+{* ../../docs_src/custom_response/tutorial002_py39.py hl[2,7] *}
/// info | Información
El mismo ejemplo de arriba, devolviendo una `HTMLResponse`, podría verse así:
-{* ../../docs_src/custom_response/tutorial003.py hl[2,7,19] *}
+{* ../../docs_src/custom_response/tutorial003_py39.py hl[2,7,19] *}
/// warning | Advertencia
Por ejemplo, podría ser algo así:
-{* ../../docs_src/custom_response/tutorial004.py hl[7,21,23] *}
+{* ../../docs_src/custom_response/tutorial004_py39.py hl[7,21,23] *}
En este ejemplo, la función `generate_html_response()` ya genera y devuelve una `Response` en lugar de devolver el HTML en un `str`.
FastAPI (de hecho Starlette) incluirá automáticamente un header Content-Length. También incluirá un header Content-Type, basado en el `media_type` y añadiendo un conjunto de caracteres para tipos de texto.
-{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
+{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
### `HTMLResponse` { #htmlresponse }
Toma algún texto o bytes y devuelve un response de texto plano.
-{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *}
+{* ../../docs_src/custom_response/tutorial005_py39.py hl[2,7,9] *}
### `JSONResponse` { #jsonresponse }
///
-{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *}
+{* ../../docs_src/custom_response/tutorial001_py39.py hl[2,7] *}
/// tip | Consejo
Puedes devolver un `RedirectResponse` directamente:
-{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *}
+{* ../../docs_src/custom_response/tutorial006_py39.py hl[2,9] *}
---
O puedes usarlo en el parámetro `response_class`:
-{* ../../docs_src/custom_response/tutorial006b.py hl[2,7,9] *}
+{* ../../docs_src/custom_response/tutorial006b_py39.py hl[2,7,9] *}
Si haces eso, entonces puedes devolver la URL directamente desde tu *path operation function*.
También puedes usar el parámetro `status_code` combinado con el parámetro `response_class`:
-{* ../../docs_src/custom_response/tutorial006c.py hl[2,7,9] *}
+{* ../../docs_src/custom_response/tutorial006c_py39.py hl[2,7,9] *}
### `StreamingResponse` { #streamingresponse }
Toma un generador `async` o un generador/iterador normal y transmite el cuerpo del response.
-{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *}
+{* ../../docs_src/custom_response/tutorial007_py39.py hl[2,14] *}
#### Usando `StreamingResponse` con objetos similares a archivos { #using-streamingresponse-with-file-like-objects }
Esto incluye muchos paquetes para interactuar con almacenamiento en la nube, procesamiento de video y otros.
-{* ../../docs_src/custom_response/tutorial008.py hl[2,10:12,14] *}
+{* ../../docs_src/custom_response/tutorial008_py39.py hl[2,10:12,14] *}
1. Esta es la función generadora. Es una "función generadora" porque contiene declaraciones `yield` dentro.
2. Al usar un bloque `with`, nos aseguramos de que el objeto similar a un archivo se cierre después de que la función generadora termine. Así, después de que termina de enviar el response.
Los responses de archivos incluirán los headers apropiados `Content-Length`, `Last-Modified` y `ETag`.
-{* ../../docs_src/custom_response/tutorial009.py hl[2,10] *}
+{* ../../docs_src/custom_response/tutorial009_py39.py hl[2,10] *}
También puedes usar el parámetro `response_class`:
-{* ../../docs_src/custom_response/tutorial009b.py hl[2,8,10] *}
+{* ../../docs_src/custom_response/tutorial009b_py39.py hl[2,8,10] *}
En este caso, puedes devolver la path del archivo directamente desde tu *path operation* function.
Podrías crear un `CustomORJSONResponse`. Lo principal que tienes que hacer es crear un método `Response.render(content)` que devuelva el contenido como `bytes`:
-{* ../../docs_src/custom_response/tutorial009c.py hl[9:14,17] *}
+{* ../../docs_src/custom_response/tutorial009c_py39.py hl[9:14,17] *}
Ahora en lugar de devolver:
En el ejemplo a continuación, **FastAPI** usará `ORJSONResponse` por defecto, en todas las *path operations*, en lugar de `JSONResponse`.
-{* ../../docs_src/custom_response/tutorial010.py hl[2,4] *}
+{* ../../docs_src/custom_response/tutorial010_py39.py hl[2,4] *}
/// tip | Consejo
Creamos una función asíncrona `lifespan()` con `yield` así:
-{* ../../docs_src/events/tutorial003.py hl[16,19] *}
+{* ../../docs_src/events/tutorial003_py39.py hl[16,19] *}
Aquí estamos simulando la operación costosa de *startup* de cargar el modelo poniendo la función del (falso) modelo en el diccionario con modelos de machine learning antes del `yield`. Este código será ejecutado **antes** de que la aplicación **comience a tomar requests**, durante el *startup*.
Lo primero que hay que notar es que estamos definiendo una función asíncrona con `yield`. Esto es muy similar a las Dependencias con `yield`.
-{* ../../docs_src/events/tutorial003.py hl[14:19] *}
+{* ../../docs_src/events/tutorial003_py39.py hl[14:19] *}
La primera parte de la función, antes del `yield`, será ejecutada **antes** de que la aplicación comience.
Eso convierte a la función en algo llamado un "**async context manager**".
-{* ../../docs_src/events/tutorial003.py hl[1,13] *}
+{* ../../docs_src/events/tutorial003_py39.py hl[1,13] *}
Un **context manager** en Python es algo que puedes usar en un statement `with`, por ejemplo, `open()` puede ser usado como un context manager:
El parámetro `lifespan` de la app de `FastAPI` toma un **async context manager**, por lo que podemos pasar nuestro nuevo `lifespan` async context manager a él.
-{* ../../docs_src/events/tutorial003.py hl[22] *}
+{* ../../docs_src/events/tutorial003_py39.py hl[22] *}
## Eventos Alternativos (obsoleto) { #alternative-events-deprecated }
Para añadir una función que debería ejecutarse antes de que la aplicación inicie, declárala con el evento `"startup"`:
-{* ../../docs_src/events/tutorial001.py hl[8] *}
+{* ../../docs_src/events/tutorial001_py39.py hl[8] *}
En este caso, la función manejadora del evento `startup` inicializará los ítems de la "base de datos" (solo un `dict`) con algunos valores.
Para añadir una función que debería ejecutarse cuando la aplicación se esté cerrando, declárala con el evento `"shutdown"`:
-{* ../../docs_src/events/tutorial002.py hl[6] *}
+{* ../../docs_src/events/tutorial002_py39.py hl[6] *}
Aquí, la función manejadora del evento `shutdown` escribirá una línea de texto `"Application shutdown"` a un archivo `log.txt`.
Podríamos descargar el JSON de OpenAPI a un archivo `openapi.json` y luego podríamos **remover ese tag prefijado** con un script como este:
-{* ../../docs_src/generate_clients/tutorial004.py *}
+{* ../../docs_src/generate_clients/tutorial004_py39.py *}
//// tab | Node.js
Cualquier request entrante a `http` o `ws` será redirigida al esquema seguro.
-{* ../../docs_src/advanced_middleware/tutorial001.py hl[2,6] *}
+{* ../../docs_src/advanced_middleware/tutorial001_py39.py hl[2,6] *}
## `TrustedHostMiddleware` { #trustedhostmiddleware }
Impone que todas las requests entrantes tengan correctamente configurado el header `Host`, para proteger contra ataques de HTTP Host Header.
-{* ../../docs_src/advanced_middleware/tutorial002.py hl[2,6:8] *}
+{* ../../docs_src/advanced_middleware/tutorial002_py39.py hl[2,6:8] *}
Se soportan los siguientes argumentos:
El middleware manejará tanto responses estándar como en streaming.
-{* ../../docs_src/advanced_middleware/tutorial003.py hl[2,6] *}
+{* ../../docs_src/advanced_middleware/tutorial003_py39.py hl[2,6] *}
Se soportan los siguientes argumentos:
Cuando creas una aplicación de **FastAPI**, hay un atributo `webhooks` que puedes usar para definir *webhooks*, de la misma manera que definirías *path operations*, por ejemplo con `@app.webhooks.post()`.
-{* ../../docs_src/openapi_webhooks/tutorial001.py hl[9:13,36:53] *}
+{* ../../docs_src/openapi_webhooks/tutorial001_py39.py hl[9:13,36:53] *}
Los webhooks que defines terminarán en el esquema de **OpenAPI** y en la interfaz automática de **documentación**.
Tienes que asegurarte de que sea único para cada operación.
-{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *}
### Usar el nombre de la *path operation function* como el operationId { #using-the-path-operation-function-name-as-the-operationid }
Deberías hacerlo después de agregar todas tus *path operations*.
-{* ../../docs_src/path_operation_advanced_configuration/tutorial002.py hl[2, 12:21, 24] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py39.py hl[2, 12:21, 24] *}
/// tip | Consejo
Para excluir una *path operation* del esquema OpenAPI generado (y por lo tanto, de los sistemas de documentación automática), utiliza el parámetro `include_in_schema` y configúralo en `False`:
-{* ../../docs_src/path_operation_advanced_configuration/tutorial003.py hl[6] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py39.py hl[6] *}
## Descripción avanzada desde el docstring { #advanced-description-from-docstring }
Este `openapi_extra` puede ser útil, por ejemplo, para declarar [Extensiones de OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
-{* ../../docs_src/path_operation_advanced_configuration/tutorial005.py hl[6] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py39.py hl[6] *}
Si abres la documentación automática de la API, tu extensión aparecerá en la parte inferior de la *path operation* específica.
Podrías hacer eso con `openapi_extra`:
-{* ../../docs_src/path_operation_advanced_configuration/tutorial006.py hl[19:36, 39:40] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *}
En este ejemplo, no declaramos ningún modelo Pydantic. De hecho, el cuerpo del request ni siquiera se <abbr title="convertido de algún formato plano, como bytes, a objetos de Python">parse</abbr> como JSON, se lee directamente como `bytes`, y la función `magic_data_reader()` sería la encargada de parsearlo de alguna manera.
Y luego puedes establecer el `status_code` en ese objeto de response *temporal*.
-{* ../../docs_src/response_change_status_code/tutorial001.py hl[1,9,12] *}
+{* ../../docs_src/response_change_status_code/tutorial001_py39.py hl[1,9,12] *}
Y luego puedes devolver cualquier objeto que necesites, como lo harías normalmente (un `dict`, un modelo de base de datos, etc.).
Y luego puedes establecer cookies en ese objeto de response *temporal*.
-{* ../../docs_src/response_cookies/tutorial002.py hl[1, 8:9] *}
+{* ../../docs_src/response_cookies/tutorial002_py39.py hl[1, 8:9] *}
Y entonces puedes devolver cualquier objeto que necesites, como normalmente lo harías (un `dict`, un modelo de base de datos, etc).
Luego establece Cookies en ella, y luego devuélvela:
-{* ../../docs_src/response_cookies/tutorial001.py hl[10:12] *}
+{* ../../docs_src/response_cookies/tutorial001_py39.py hl[10:12] *}
/// tip | Consejo
Podrías poner tu contenido XML en un string, poner eso en un `Response`, y devolverlo:
-{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
+{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
## Notas { #notes }
Y luego puedes establecer headers en ese objeto de response *temporal*.
-{* ../../docs_src/response_headers/tutorial002.py hl[1, 7:8] *}
+{* ../../docs_src/response_headers/tutorial002_py39.py hl[1, 7:8] *}
Y luego puedes devolver cualquier objeto que necesites, como harías normalmente (un `dict`, un modelo de base de datos, etc).
Crea un response como se describe en [Retorna un Response Directamente](response-directly.md){.internal-link target=_blank} y pasa los headers como un parámetro adicional:
-{* ../../docs_src/response_headers/tutorial001.py hl[10:12] *}
+{* ../../docs_src/response_headers/tutorial001_py39.py hl[10:12] *}
/// note | Detalles Técnicos
//// tab | Pydantic v2
-{* ../../docs_src/settings/tutorial001.py hl[2,5:8,11] *}
+{* ../../docs_src/settings/tutorial001_py39.py hl[2,5:8,11] *}
////
///
-{* ../../docs_src/settings/tutorial001_pv1.py hl[2,5:8,11] *}
+{* ../../docs_src/settings/tutorial001_pv1_py39.py hl[2,5:8,11] *}
////
Luego puedes usar el nuevo objeto `settings` en tu aplicación:
-{* ../../docs_src/settings/tutorial001.py hl[18:20] *}
+{* ../../docs_src/settings/tutorial001_py39.py hl[18:20] *}
### Ejecutar el servidor { #run-the-server }
Por ejemplo, podrías tener un archivo `config.py` con:
-{* ../../docs_src/settings/app01/config.py *}
+{* ../../docs_src/settings/app01_py39/config.py *}
Y luego usarlo en un archivo `main.py`:
-{* ../../docs_src/settings/app01/main.py hl[3,11:13] *}
+{* ../../docs_src/settings/app01_py39/main.py hl[3,11:13] *}
/// tip | Consejo
Primero, crea la aplicación principal de nivel superior de **FastAPI**, y sus *path operations*:
-{* ../../docs_src/sub_applications/tutorial001.py hl[3, 6:8] *}
+{* ../../docs_src/sub_applications/tutorial001_py39.py hl[3, 6:8] *}
### Sub-aplicación { #sub-application }
Esta sub-aplicación es solo otra aplicación estándar de FastAPI, pero es la que se "montará":
-{* ../../docs_src/sub_applications/tutorial001.py hl[11, 14:16] *}
+{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 14:16] *}
### Montar la sub-aplicación { #mount-the-sub-application }
En este caso, se montará en el path `/subapi`:
-{* ../../docs_src/sub_applications/tutorial001.py hl[11, 19] *}
+{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 19] *}
### Revisa la documentación automática de la API { #check-the-automatic-api-docs }
* Declara un parámetro `Request` en la *path operation* que devolverá una plantilla.
* Usa los `templates` que creaste para renderizar y devolver un `TemplateResponse`, pasa el nombre de la plantilla, el objeto de request, y un diccionario "context" con pares clave-valor que se usarán dentro de la plantilla Jinja2.
-{* ../../docs_src/templates/tutorial001.py hl[4,11,15:18] *}
+{* ../../docs_src/templates/tutorial001_py39.py hl[4,11,15:18] *}
/// note | Nota
Cuando necesitas que `lifespan` se ejecute en tus tests, puedes usar el `TestClient` con un statement `with`:
-{* ../../docs_src/app_testing/tutorial004.py hl[9:15,18,27:28,30:32,41:43] *}
+{* ../../docs_src/app_testing/tutorial004_py39.py hl[9:15,18,27:28,30:32,41:43] *}
Puedes leer más detalles sobre ["Ejecutar lifespan en tests en el sitio oficial de documentación de Starlette."](https://www.starlette.dev/lifespan/#running-lifespan-in-tests)
Para los eventos obsoletos `startup` y `shutdown`, puedes usar el `TestClient` así:
-{* ../../docs_src/app_testing/tutorial003.py hl[9:12,20:24] *}
+{* ../../docs_src/app_testing/tutorial003_py39.py hl[9:12,20:24] *}
Para esto, usas el `TestClient` en un statement `with`, conectándote al WebSocket:
-{* ../../docs_src/app_testing/tutorial002.py hl[27:31] *}
+{* ../../docs_src/app_testing/tutorial002_py39.py hl[27:31] *}
/// note | Nota
Para eso necesitas acceder al request directamente.
-{* ../../docs_src/using_request_directly/tutorial001.py hl[1,7:8] *}
+{* ../../docs_src/using_request_directly/tutorial001_py39.py hl[1,7:8] *}
Al declarar un parámetro de *path operation function* con el tipo siendo `Request`, **FastAPI** sabrá pasar el `Request` en ese parámetro.
Pero es la forma más sencilla de enfocarse en el lado del servidor de WebSockets y tener un ejemplo funcional:
-{* ../../docs_src/websockets/tutorial001.py hl[2,6:38,41:43] *}
+{* ../../docs_src/websockets/tutorial001_py39.py hl[2,6:38,41:43] *}
## Crear un `websocket` { #create-a-websocket }
En tu aplicación de **FastAPI**, crea un `websocket`:
-{* ../../docs_src/websockets/tutorial001.py hl[1,46:47] *}
+{* ../../docs_src/websockets/tutorial001_py39.py hl[1,46:47] *}
/// note | Detalles Técnicos
En tu ruta de WebSocket puedes `await` para recibir mensajes y enviar mensajes.
-{* ../../docs_src/websockets/tutorial001.py hl[48:52] *}
+{* ../../docs_src/websockets/tutorial001_py39.py hl[48:52] *}
Puedes recibir y enviar datos binarios, de texto y JSON.
Y luego móntala bajo un path.
-{* ../../docs_src/wsgi/tutorial001.py hl[2:3,3] *}
+{* ../../docs_src/wsgi/tutorial001_py39.py hl[2:3,3] *}
## Revisa { #check-it }
Por ejemplo:
-{* ../../docs_src/conditional_openapi/tutorial001.py hl[6,11] *}
+{* ../../docs_src/conditional_openapi/tutorial001_py39.py hl[6,11] *}
Aquí declaramos la configuración `openapi_url` con el mismo valor por defecto de `"/openapi.json"`.
Pero puedes desactivarlo estableciendo `syntaxHighlight` en `False`:
-{* ../../docs_src/configure_swagger_ui/tutorial001.py hl[3] *}
+{* ../../docs_src/configure_swagger_ui/tutorial001_py39.py hl[3] *}
...y entonces Swagger UI ya no mostrará el resaltado de sintaxis:
De la misma manera, podrías configurar el tema del resaltado de sintaxis con la clave `"syntaxHighlight.theme"` (ten en cuenta que tiene un punto en el medio):
-{* ../../docs_src/configure_swagger_ui/tutorial002.py hl[3] *}
+{* ../../docs_src/configure_swagger_ui/tutorial002_py39.py hl[3] *}
Esa configuración cambiaría el tema de color del resaltado de sintaxis:
Por ejemplo, para desactivar `deepLinking` podrías pasar estas configuraciones a `swagger_ui_parameters`:
-{* ../../docs_src/configure_swagger_ui/tutorial003.py hl[3] *}
+{* ../../docs_src/configure_swagger_ui/tutorial003_py39.py hl[3] *}
## Otros parámetros de Swagger UI { #other-swagger-ui-parameters }
Para desactivarlos, establece sus URLs en `None` cuando crees tu aplicación de `FastAPI`:
-{* ../../docs_src/custom_docs_ui/tutorial001.py hl[8] *}
+{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[8] *}
### Incluye la documentación personalizada { #include-the-custom-docs }
Y de manera similar para ReDoc...
-{* ../../docs_src/custom_docs_ui/tutorial001.py hl[2:6,11:19,22:24,27:33] *}
+{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[2:6,11:19,22:24,27:33] *}
/// tip | Consejo
Ahora, para poder probar que todo funciona, crea una *path operation*:
-{* ../../docs_src/custom_docs_ui/tutorial001.py hl[36:38] *}
+{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[36:38] *}
### Pruébalo { #test-it }
* Importa `StaticFiles`.
* "Monta" una instance de `StaticFiles()` en un path específico.
-{* ../../docs_src/custom_docs_ui/tutorial002.py hl[7,11] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[7,11] *}
### Prueba los archivos estáticos { #test-the-static-files }
Para desactivarlos, establece sus URLs en `None` cuando crees tu aplicación de `FastAPI`:
-{* ../../docs_src/custom_docs_ui/tutorial002.py hl[9] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[9] *}
### Incluye la documentación personalizada para archivos estáticos { #include-the-custom-docs-for-static-files }
Y de manera similar para ReDoc...
-{* ../../docs_src/custom_docs_ui/tutorial002.py hl[2:6,14:22,25:27,30:36] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[2:6,14:22,25:27,30:36] *}
/// tip | Consejo
Ahora, para poder probar que todo funciona, crea una *path operation*:
-{* ../../docs_src/custom_docs_ui/tutorial002.py hl[39:41] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[39:41] *}
### Prueba la UI de Archivos Estáticos { #test-static-files-ui }
Primero, escribe toda tu aplicación **FastAPI** como normalmente:
-{* ../../docs_src/extending_openapi/tutorial001.py hl[1,4,7:9] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[1,4,7:9] *}
### Generar el esquema de OpenAPI { #generate-the-openapi-schema }
Luego, usa la misma función de utilidad para generar el esquema de OpenAPI, dentro de una función `custom_openapi()`:
-{* ../../docs_src/extending_openapi/tutorial001.py hl[2,15:21] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[2,15:21] *}
### Modificar el esquema de OpenAPI { #modify-the-openapi-schema }
Ahora puedes añadir la extensión de ReDoc, agregando un `x-logo` personalizado al "objeto" `info` en el esquema de OpenAPI:
-{* ../../docs_src/extending_openapi/tutorial001.py hl[22:24] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[22:24] *}
### Cachear el esquema de OpenAPI { #cache-the-openapi-schema }
Se generará solo una vez, y luego se usará el mismo esquema cacheado para las siguientes requests.
-{* ../../docs_src/extending_openapi/tutorial001.py hl[13:14,25:26] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[13:14,25:26] *}
### Sobrescribir el método { #override-the-method }
Ahora puedes reemplazar el método `.openapi()` por tu nueva función.
-{* ../../docs_src/extending_openapi/tutorial001.py hl[29] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[29] *}
### Revisa { #check-it }
Aquí tienes una pequeña vista previa de cómo podrías integrar Strawberry con FastAPI:
-{* ../../docs_src/graphql/tutorial001.py hl[3,22,25] *}
+{* ../../docs_src/graphql/tutorial001_py39.py hl[3,22,25] *}
Puedes aprender más sobre Strawberry en la <a href="https://strawberry.rocks/" class="external-link" target="_blank">documentación de Strawberry</a>.
Comencemos con un ejemplo simple:
-{* ../../docs_src/python_types/tutorial001.py *}
+{* ../../docs_src/python_types/tutorial001_py39.py *}
Llamar a este programa genera:
* Convierte la primera letra de cada uno a mayúsculas con `title()`.
* <abbr title="Los une, como uno. Con el contenido de uno después del otro.">Concatena</abbr> ambos con un espacio en el medio.
-{* ../../docs_src/python_types/tutorial001.py hl[2] *}
+{* ../../docs_src/python_types/tutorial001_py39.py hl[2] *}
### Edítalo { #edit-it }
Esas son las "anotaciones de tipos":
-{* ../../docs_src/python_types/tutorial002.py hl[1] *}
+{* ../../docs_src/python_types/tutorial002_py39.py hl[1] *}
Eso no es lo mismo que declarar valores predeterminados como sería con:
Revisa esta función, ya tiene anotaciones de tipos:
-{* ../../docs_src/python_types/tutorial003.py hl[1] *}
+{* ../../docs_src/python_types/tutorial003_py39.py hl[1] *}
Porque el editor conoce los tipos de las variables, no solo obtienes autocompletado, también obtienes chequeo de errores:
Ahora sabes que debes corregirlo, convertir `age` a un string con `str(age)`:
-{* ../../docs_src/python_types/tutorial004.py hl[2] *}
+{* ../../docs_src/python_types/tutorial004_py39.py hl[2] *}
## Declaración de tipos { #declaring-types }
* `bool`
* `bytes`
-{* ../../docs_src/python_types/tutorial005.py hl[1] *}
+{* ../../docs_src/python_types/tutorial005_py39.py hl[1] *}
### Tipos genéricos con parámetros de tipo { #generic-types-with-type-parameters }
Por ejemplo, vamos a definir una variable para ser una `list` de `str`.
-//// tab | Python 3.9+
-
Declara la variable, con la misma sintaxis de dos puntos (`:`).
Como tipo, pon `list`.
Como la lista es un tipo que contiene algunos tipos internos, los pones entre corchetes:
-```Python hl_lines="1"
-{!> ../../docs_src/python_types/tutorial006_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-De `typing`, importa `List` (con una `L` mayúscula):
-
-```Python hl_lines="1"
-{!> ../../docs_src/python_types/tutorial006.py!}
-```
-
-Declara la variable, con la misma sintaxis de dos puntos (`:`).
-
-Como tipo, pon el `List` que importaste de `typing`.
-
-Como la lista es un tipo que contiene algunos tipos internos, los pones entre corchetes:
-
-```Python hl_lines="4"
-{!> ../../docs_src/python_types/tutorial006.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial006_py39.py hl[1] *}
/// info | Información
Esos tipos internos en los corchetes se denominan "parámetros de tipo".
-En este caso, `str` es el parámetro de tipo pasado a `List` (o `list` en Python 3.9 y superior).
+En este caso, `str` es el parámetro de tipo pasado a `list`.
///
Eso significa: "la variable `items` es una `list`, y cada uno de los ítems en esta lista es un `str`".
-/// tip | Consejo
-
-Si usas Python 3.9 o superior, no tienes que importar `List` de `typing`, puedes usar el mismo tipo `list` regular en su lugar.
-
-///
-
Al hacer eso, tu editor puede proporcionar soporte incluso mientras procesa elementos de la lista:
<img src="/img/python-types/image05.png">
Harías lo mismo para declarar `tuple`s y `set`s:
-//// tab | Python 3.9+
-
-```Python hl_lines="1"
-{!> ../../docs_src/python_types/tutorial007_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial007.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial007_py39.py hl[1] *}
Esto significa:
El segundo parámetro de tipo es para los valores del `dict`:
-//// tab | Python 3.9+
-
-```Python hl_lines="1"
-{!> ../../docs_src/python_types/tutorial008_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial008.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial008_py39.py hl[1] *}
Esto significa:
////
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial008b.py!}
+{!> ../../docs_src/python_types/tutorial008b_py39.py!}
```
////
En Python 3.6 y posteriores (incluyendo Python 3.10) puedes declararlo importando y usando `Optional` del módulo `typing`.
```Python hl_lines="1 4"
-{!../../docs_src/python_types/tutorial009.py!}
+{!../../docs_src/python_types/tutorial009_py39.py!}
```
Usar `Optional[str]` en lugar de solo `str` te permitirá al editor ayudarte a detectar errores donde podrías estar asumiendo que un valor siempre es un `str`, cuando en realidad también podría ser `None`.
////
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial009.py!}
+{!> ../../docs_src/python_types/tutorial009_py39.py!}
```
////
-//// tab | Python 3.8+ alternativa
+//// tab | Python 3.9+ alternativa
```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial009b.py!}
+{!> ../../docs_src/python_types/tutorial009b_py39.py!}
```
////
Como ejemplo, tomemos esta función:
-{* ../../docs_src/python_types/tutorial009c.py hl[1,4] *}
+{* ../../docs_src/python_types/tutorial009c_py39.py hl[1,4] *}
El parámetro `name` está definido como `Optional[str]`, pero **no es opcional**, no puedes llamar a la función sin el parámetro:
* `set`
* `dict`
-Y lo mismo que con Python 3.8, desde el módulo `typing`:
+Y, como con versiones anteriores de Python, desde el módulo `typing`:
* `Union`
-* `Optional` (lo mismo que con Python 3.8)
+* `Optional`
* ...y otros.
En Python 3.10, como alternativa a usar los genéricos `Union` y `Optional`, puedes usar la <abbr title='también llamado "operador OR a nivel de bits", pero ese significado no es relevante aquí'>barra vertical (`|`)</abbr> para declarar uniones de tipos, eso es mucho mejor y más simple.
* `set`
* `dict`
-Y lo mismo que con Python 3.8, desde el módulo `typing`:
+Y generics desde el módulo `typing`:
* `Union`
* `Optional`
////
-//// tab | Python 3.8+
-
-* `List`
-* `Tuple`
-* `Set`
-* `Dict`
-* `Union`
-* `Optional`
-* ...y otros.
-
-////
-
### Clases como tipos { #classes-as-types }
También puedes declarar una clase como el tipo de una variable.
Digamos que tienes una clase `Person`, con un nombre:
-{* ../../docs_src/python_types/tutorial010.py hl[1:3] *}
+{* ../../docs_src/python_types/tutorial010_py39.py hl[1:3] *}
Luego puedes declarar una variable para que sea de tipo `Person`:
-{* ../../docs_src/python_types/tutorial010.py hl[6] *}
+{* ../../docs_src/python_types/tutorial010_py39.py hl[6] *}
Y luego, nuevamente, obtienes todo el soporte del editor:
Un ejemplo de la documentación oficial de Pydantic:
-//// tab | Python 3.10+
-
-```Python
-{!> ../../docs_src/python_types/tutorial011_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python
-{!> ../../docs_src/python_types/tutorial011_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python
-{!> ../../docs_src/python_types/tutorial011.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial011_py310.py *}
/// info | Información
Python también tiene una funcionalidad que permite poner **<abbr title="Datos sobre los datos, en este caso, información sobre el tipo, por ejemplo, una descripción.">metadatos</abbr> adicional** en estas anotaciones de tipos usando `Annotated`.
-//// tab | Python 3.9+
-
-En Python 3.9, `Annotated` es parte de la standard library, así que puedes importarlo desde `typing`.
+Desde Python 3.9, `Annotated` es parte de la standard library, así que puedes importarlo desde `typing`.
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial013_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-En versiones por debajo de Python 3.9, importas `Annotated` de `typing_extensions`.
-
-Ya estará instalado con **FastAPI**.
-
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial013.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial013_py39.py hl[1,4] *}
Python en sí no hace nada con este `Annotated`. Y para los editores y otras herramientas, el tipo sigue siendo `str`.
Primero, importa `BackgroundTasks` y define un parámetro en tu *path operation function* con una declaración de tipo de `BackgroundTasks`:
-{* ../../docs_src/background_tasks/tutorial001.py hl[1,13] *}
+{* ../../docs_src/background_tasks/tutorial001_py39.py hl[1,13] *}
**FastAPI** creará el objeto de tipo `BackgroundTasks` por ti y lo pasará como ese parámetro.
Y como la operación de escritura no usa `async` y `await`, definimos la función con un `def` normal:
-{* ../../docs_src/background_tasks/tutorial001.py hl[6:9] *}
+{* ../../docs_src/background_tasks/tutorial001_py39.py hl[6:9] *}
## Agregar la tarea en segundo plano { #add-the-background-task }
Dentro de tu *path operation function*, pasa tu función de tarea al objeto de *background tasks* con el método `.add_task()`:
-{* ../../docs_src/background_tasks/tutorial001.py hl[14] *}
+{* ../../docs_src/background_tasks/tutorial001_py39.py hl[14] *}
`.add_task()` recibe como argumentos:
Pero Python tiene una forma específica de declarar listas con tipos internos, o "parámetros de tipo":
-### Importar `List` de typing { #import-typings-list }
-
-En Python 3.9 y superior, puedes usar el `list` estándar para declarar estas anotaciones de tipo como veremos a continuación. 💡
-
-Pero en versiones de Python anteriores a 3.9 (desde 3.6 en adelante), primero necesitas importar `List` del módulo `typing` estándar de Python:
-
-{* ../../docs_src/body_nested_models/tutorial002.py hl[1] *}
-
### Declarar una `list` con un parámetro de tipo { #declare-a-list-with-a-type-parameter }
-Para declarar tipos que tienen parámetros de tipo (tipos internos), como `list`, `dict`, `tuple`:
-
-* Si estás en una versión de Python inferior a 3.9, importa su versión equivalente del módulo `typing`
-* Pasa el/los tipo(s) interno(s) como "parámetros de tipo" usando corchetes: `[` y `]`
-
-En Python 3.9 sería:
+Para declarar tipos que tienen parámetros de tipo (tipos internos), como `list`, `dict`, `tuple`,
+pasa el/los tipo(s) interno(s) como "parámetros de tipo" usando corchetes: `[` y `]`
```Python
my_list: list[str]
```
-En versiones de Python anteriores a 3.9, sería:
-
-```Python
-from typing import List
-
-my_list: List[str]
-```
-
Eso es toda la sintaxis estándar de Python para declaraciones de tipo.
Usa esa misma sintaxis estándar para atributos de modelos con tipos internos.
Si el valor superior del cuerpo JSON que esperas es un `array` JSON (una `list` en Python), puedes declarar el tipo en el parámetro de la función, al igual que en los modelos Pydantic:
-```Python
-images: List[Image]
-```
-
-o en Python 3.9 y superior:
-
```Python
images: list[Image]
```
FastAPI sabrá que el valor de `q` no es requerido debido al valor por defecto `= None`.
-El `str | None` (Python 3.10+) o `Union` en `Union[str, None]` (Python 3.8+) no es utilizado por FastAPI para determinar que el valor no es requerido, sabrá que no es requerido porque tiene un valor por defecto de `= None`.
+El `str | None` (Python 3.10+) o `Union` en `Union[str, None]` (Python 3.9+) no es utilizado por FastAPI para determinar que el valor no es requerido, sabrá que no es requerido porque tiene un valor por defecto de `= None`.
Pero agregar las anotaciones de tipos permitirá que tu editor te brinde un mejor soporte y detecte errores.
* Métodos HTTP específicos (`POST`, `PUT`) o todos ellos con el comodín `"*"`.
* Headers HTTP específicos o todos ellos con el comodín `"*"`.
-{* ../../docs_src/cors/tutorial001.py hl[2,6:11,13:19] *}
+{* ../../docs_src/cors/tutorial001_py39.py hl[2,6:11,13:19] *}
Los parámetros predeterminados utilizados por la implementación de `CORSMiddleware` son restrictivos por defecto, por lo que necesitarás habilitar explícitamente orígenes, métodos o headers particulares para que los navegadores estén permitidos de usarlos en un contexto de Cross-Domain.
En tu aplicación de FastAPI, importa y ejecuta `uvicorn` directamente:
-{* ../../docs_src/debugging/tutorial001.py hl[1,15] *}
+{* ../../docs_src/debugging/tutorial001_py39.py hl[1,15] *}
### Acerca de `__name__ == "__main__"` { #about-name-main }
Nota cómo escribimos `CommonQueryParams` dos veces en el código anterior:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
-//// tab | Python 3.8+ sin `Annotated`
+//// tab | Python 3.9+ sin `Annotated`
/// tip | Consejo
En este caso, el primer `CommonQueryParams`, en:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, ...
////
-//// tab | Python 3.8+ sin `Annotated`
+//// tab | Python 3.9+ sin `Annotated`
/// tip | Consejo
De hecho, podrías escribir simplemente:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[Any, Depends(CommonQueryParams)]
////
-//// tab | Python 3.8+ sin `Annotated`
+//// tab | Python 3.9+ sin `Annotated`
/// tip | Consejo
Pero ves que estamos teniendo algo de repetición de código aquí, escribiendo `CommonQueryParams` dos veces:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
-//// tab | Python 3.8+ sin `Annotated`
+//// tab | Python 3.9+ sin `Annotated`
/// tip | Consejo
En lugar de escribir:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
-//// tab | Python 3.8+ sin `Annotated`
+//// tab | Python 3.9+ sin `Annotated`
/// tip | Consejo
...escribes:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends()]
////
-//// tab | Python 3.8 sin `Annotated`
+//// tab | Python 3.9+ sin `Annotated`
/// tip | Consejo
Solo el código anterior e incluyendo la declaración `yield` se ejecuta antes de crear un response:
-{* ../../docs_src/dependencies/tutorial007.py hl[2:4] *}
+{* ../../docs_src/dependencies/tutorial007_py39.py hl[2:4] *}
El valor generado es lo que se inyecta en *path operations* y otras dependencias:
-{* ../../docs_src/dependencies/tutorial007.py hl[4] *}
+{* ../../docs_src/dependencies/tutorial007_py39.py hl[4] *}
El código posterior a la declaración `yield` se ejecuta después del response:
-{* ../../docs_src/dependencies/tutorial007.py hl[5:6] *}
+{* ../../docs_src/dependencies/tutorial007_py39.py hl[5:6] *}
/// tip | Consejo
Del mismo modo, puedes usar `finally` para asegurarte de que los pasos de salida se ejecuten, sin importar si hubo una excepción o no.
-{* ../../docs_src/dependencies/tutorial007.py hl[3,5] *}
+{* ../../docs_src/dependencies/tutorial007_py39.py hl[3,5] *}
## Sub-dependencias con `yield` { #sub-dependencies-with-yield }
También puedes usarlos dentro de las dependencias de **FastAPI** con `yield` usando
`with` o `async with` en la función de dependencia:
-{* ../../docs_src/dependencies/tutorial010.py hl[1:9,13] *}
+{* ../../docs_src/dependencies/tutorial010_py39.py hl[1:9,13] *}
/// tip | Consejo
En ese caso, se aplicarán a todas las *path operations* en la aplicación:
-{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[16] *}
+{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[17] *}
Y todas las ideas en la sección sobre [agregar `dependencies` a los *path operation decorators*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} siguen aplicándose, pero en este caso, a todas las *path operations* en la app.
En un escenario avanzado donde sabes que necesitas que la dependencia se llame en cada paso (posiblemente varias veces) en el mismo request en lugar de usar el valor "cache", puedes establecer el parámetro `use_cache=False` al usar `Depends`:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python hl_lines="1"
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
////
-//// tab | Python 3.8+ sin Anotaciones
+//// tab | Python 3.9+ sin Anotaciones
/// tip | Consejo
El archivo FastAPI más simple podría verse así:
-{* ../../docs_src/first_steps/tutorial001.py *}
+{* ../../docs_src/first_steps/tutorial001_py39.py *}
Copia eso en un archivo `main.py`.
### Paso 1: importa `FastAPI` { #step-1-import-fastapi }
-{* ../../docs_src/first_steps/tutorial001.py hl[1] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[1] *}
`FastAPI` es una clase de Python que proporciona toda la funcionalidad para tu API.
### Paso 2: crea una "instance" de `FastAPI` { #step-2-create-a-fastapi-instance }
-{* ../../docs_src/first_steps/tutorial001.py hl[3] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[3] *}
Aquí la variable `app` será una "instance" de la clase `FastAPI`.
#### Define un *path operation decorator* { #define-a-path-operation-decorator }
-{* ../../docs_src/first_steps/tutorial001.py hl[6] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[6] *}
El `@app.get("/")` le dice a **FastAPI** que la función justo debajo se encarga de manejar requests que vayan a:
* **operation**: es `get`.
* **function**: es la función debajo del "decorador" (debajo de `@app.get("/")`).
-{* ../../docs_src/first_steps/tutorial001.py hl[7] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[7] *}
Esta es una función de Python.
También podrías definirla como una función normal en lugar de `async def`:
-{* ../../docs_src/first_steps/tutorial003.py hl[7] *}
+{* ../../docs_src/first_steps/tutorial003_py39.py hl[7] *}
/// note | Nota
### Paso 5: retorna el contenido { #step-5-return-the-content }
-{* ../../docs_src/first_steps/tutorial001.py hl[8] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[8] *}
Puedes retornar un `dict`, `list`, valores singulares como `str`, `int`, etc.
### Importa `HTTPException` { #import-httpexception }
-{* ../../docs_src/handling_errors/tutorial001.py hl[1] *}
+{* ../../docs_src/handling_errors/tutorial001_py39.py hl[1] *}
### Lanza un `HTTPException` en tu código { #raise-an-httpexception-in-your-code }
En este ejemplo, cuando el cliente solicita un ítem por un ID que no existe, lanza una excepción con un código de estado de `404`:
-{* ../../docs_src/handling_errors/tutorial001.py hl[11] *}
+{* ../../docs_src/handling_errors/tutorial001_py39.py hl[11] *}
### El response resultante { #the-resulting-response }
Pero en caso de que los necesites para un escenario avanzado, puedes agregar headers personalizados:
-{* ../../docs_src/handling_errors/tutorial002.py hl[14] *}
+{* ../../docs_src/handling_errors/tutorial002_py39.py hl[14] *}
## Instalar manejadores de excepciones personalizados { #install-custom-exception-handlers }
Podrías agregar un manejador de excepciones personalizado con `@app.exception_handler()`:
-{* ../../docs_src/handling_errors/tutorial003.py hl[5:7,13:18,24] *}
+{* ../../docs_src/handling_errors/tutorial003_py39.py hl[5:7,13:18,24] *}
Aquí, si solicitas `/unicorns/yolo`, la *path operation* lanzará un `UnicornException`.
El manejador de excepciones recibirá un `Request` y la excepción.
-{* ../../docs_src/handling_errors/tutorial004.py hl[2,14:19] *}
+{* ../../docs_src/handling_errors/tutorial004_py39.py hl[2,14:19] *}
Ahora, si vas a `/items/foo`, en lugar de obtener el error JSON por defecto con:
Por ejemplo, podrías querer devolver un response de texto plano en lugar de JSON para estos errores:
-{* ../../docs_src/handling_errors/tutorial004.py hl[3:4,9:11,25] *}
+{* ../../docs_src/handling_errors/tutorial004_py39.py hl[3:4,9:11,25] *}
/// note | Nota Técnica
Podrías usarlo mientras desarrollas tu aplicación para registrar el body y depurarlo, devolverlo al usuario, etc.
-{* ../../docs_src/handling_errors/tutorial005.py hl[14] *}
+{* ../../docs_src/handling_errors/tutorial005_py39.py hl[14] *}
Ahora intenta enviar un ítem inválido como:
Si quieres usar la excepción junto con los mismos manejadores de excepciones predeterminados de **FastAPI**, puedes importar y reutilizar los manejadores de excepciones predeterminados de `fastapi.exception_handlers`:
-{* ../../docs_src/handling_errors/tutorial006.py hl[2:5,15,21] *}
+{* ../../docs_src/handling_errors/tutorial006_py39.py hl[2:5,15,21] *}
En este ejemplo solo estás `print`eando el error con un mensaje muy expresivo, pero te haces una idea. Puedes usar la excepción y luego simplemente reutilizar los manejadores de excepciones predeterminados.
Puedes configurarlos de la siguiente manera:
-{* ../../docs_src/metadata/tutorial001.py hl[3:16, 19:32] *}
+{* ../../docs_src/metadata/tutorial001_py39.py hl[3:16, 19:32] *}
/// tip | Consejo
Por ejemplo:
-{* ../../docs_src/metadata/tutorial001_1.py hl[31] *}
+{* ../../docs_src/metadata/tutorial001_1_py39.py hl[31] *}
## Metadata para etiquetas { #metadata-for-tags }
Crea metadata para tus etiquetas y pásala al parámetro `openapi_tags`:
-{* ../../docs_src/metadata/tutorial004.py hl[3:16,18] *}
+{* ../../docs_src/metadata/tutorial004_py39.py hl[3:16,18] *}
Nota que puedes utilizar Markdown dentro de las descripciones, por ejemplo "login" se mostrará en negrita (**login**) y "fancy" se mostrará en cursiva (_fancy_).
Usa el parámetro `tags` con tus *path operations* (y `APIRouter`s) para asignarlas a diferentes etiquetas:
-{* ../../docs_src/metadata/tutorial004.py hl[21,26] *}
+{* ../../docs_src/metadata/tutorial004_py39.py hl[21,26] *}
/// info | Información
Por ejemplo, para configurarlo para que se sirva en `/api/v1/openapi.json`:
-{* ../../docs_src/metadata/tutorial002.py hl[3] *}
+{* ../../docs_src/metadata/tutorial002_py39.py hl[3] *}
Si quieres deshabilitar el esquema OpenAPI completamente, puedes establecer `openapi_url=None`, eso también deshabilitará las interfaces de usuario de documentación que lo usan.
Por ejemplo, para configurar Swagger UI para que se sirva en `/documentation` y deshabilitar ReDoc:
-{* ../../docs_src/metadata/tutorial003.py hl[3] *}
+{* ../../docs_src/metadata/tutorial003_py39.py hl[3] *}
* Luego devuelve la `response` generada por la correspondiente *path operation*.
* Puedes entonces modificar aún más la `response` antes de devolverla.
-{* ../../docs_src/middleware/tutorial001.py hl[8:9,11,14] *}
+{* ../../docs_src/middleware/tutorial001_py39.py hl[8:9,11,14] *}
/// tip | Consejo
Por ejemplo, podrías añadir un custom header `X-Process-Time` que contenga el tiempo en segundos que tomó procesar la request y generar una response:
-{* ../../docs_src/middleware/tutorial001.py hl[10,12:13] *}
+{* ../../docs_src/middleware/tutorial001_py39.py hl[10,12:13] *}
/// tip | Consejo
**FastAPI** soporta eso de la misma manera que con strings normales:
-{* ../../docs_src/path_operation_configuration/tutorial002b.py hl[1,8:10,13,18] *}
+{* ../../docs_src/path_operation_configuration/tutorial002b_py39.py hl[1,8:10,13,18] *}
## Resumen y Descripción { #summary-and-description }
Si necesitas marcar una *path operation* como <abbr title="obsoleta, se recomienda no usarla">deprecated</abbr>, pero sin eliminarla, pasa el parámetro `deprecated`:
-{* ../../docs_src/path_operation_configuration/tutorial006.py hl[16] *}
+{* ../../docs_src/path_operation_configuration/tutorial006_py39.py hl[16] *}
Se marcará claramente como deprecado en la documentación interactiva:
Así que puedes declarar tu función como:
-{* ../../docs_src/path_params_numeric_validations/tutorial002.py hl[7] *}
+{* ../../docs_src/path_params_numeric_validations/tutorial002_py39.py hl[7] *}
Pero ten en cuenta que si usas `Annotated`, no tendrás este problema, no importará ya que no estás usando los valores por defecto de los parámetros de la función para `Query()` o `Path()`.
Python no hará nada con ese `*`, pero sabrá que todos los parámetros siguientes deben ser llamados como argumentos de palabras clave (parejas key-value), también conocidos como <abbr title="De: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>. Incluso si no tienen un valor por defecto.
-{* ../../docs_src/path_params_numeric_validations/tutorial003.py hl[7] *}
+{* ../../docs_src/path_params_numeric_validations/tutorial003_py39.py hl[7] *}
### Mejor con `Annotated` { #better-with-annotated }
Puedes declarar "parámetros" o "variables" de path con la misma sintaxis que se usa en los format strings de Python:
-{* ../../docs_src/path_params/tutorial001.py hl[6:7] *}
+{* ../../docs_src/path_params/tutorial001_py39.py hl[6:7] *}
El valor del parámetro de path `item_id` se pasará a tu función como el argumento `item_id`.
Puedes declarar el tipo de un parámetro de path en la función, usando anotaciones de tipos estándar de Python:
-{* ../../docs_src/path_params/tutorial002.py hl[7] *}
+{* ../../docs_src/path_params/tutorial002_py39.py hl[7] *}
En este caso, `item_id` se declara como un `int`.
Debido a que las *path operations* se evalúan en orden, necesitas asegurarte de que el path para `/users/me` se declara antes que el de `/users/{user_id}`:
-{* ../../docs_src/path_params/tutorial003.py hl[6,11] *}
+{* ../../docs_src/path_params/tutorial003_py39.py hl[6,11] *}
De lo contrario, el path para `/users/{user_id}` también coincidiría para `/users/me`, "pensando" que está recibiendo un parámetro `user_id` con un valor de `"me"`.
De manera similar, no puedes redefinir una path operation:
-{* ../../docs_src/path_params/tutorial003b.py hl[6,11] *}
+{* ../../docs_src/path_params/tutorial003b_py39.py hl[6,11] *}
La primera siempre será utilizada ya que el path coincide primero.
Luego crea atributos de clase con valores fijos, que serán los valores válidos disponibles:
-{* ../../docs_src/path_params/tutorial005.py hl[1,6:9] *}
-
-/// info | Información
-
-<a href="https://docs.python.org/3/library/enum.html" class="external-link" target="_blank">Las enumeraciones (o enums) están disponibles en Python</a> desde la versión 3.4.
-
-///
+{* ../../docs_src/path_params/tutorial005_py39.py hl[1,6:9] *}
/// tip | Consejo
Luego crea un *path parameter* con una anotación de tipo usando la clase enum que creaste (`ModelName`):
-{* ../../docs_src/path_params/tutorial005.py hl[16] *}
+{* ../../docs_src/path_params/tutorial005_py39.py hl[16] *}
### Revisa la documentación { #check-the-docs }
Puedes compararlo con el *miembro* de enumeración en tu enum creada `ModelName`:
-{* ../../docs_src/path_params/tutorial005.py hl[17] *}
+{* ../../docs_src/path_params/tutorial005_py39.py hl[17] *}
#### Obtener el valor de *enumeración* { #get-the-enumeration-value }
Puedes obtener el valor actual (un `str` en este caso) usando `model_name.value`, o en general, `your_enum_member.value`:
-{* ../../docs_src/path_params/tutorial005.py hl[20] *}
+{* ../../docs_src/path_params/tutorial005_py39.py hl[20] *}
/// tip | Consejo
Serán convertidos a sus valores correspondientes (cadenas en este caso) antes de devolverlos al cliente:
-{* ../../docs_src/path_params/tutorial005.py hl[18,21,23] *}
+{* ../../docs_src/path_params/tutorial005_py39.py hl[18,21,23] *}
En tu cliente recibirás un response JSON como:
Así que, puedes usarlo con:
-{* ../../docs_src/path_params/tutorial004.py hl[6] *}
+{* ../../docs_src/path_params/tutorial004_py39.py hl[6] *}
/// tip | Consejo
////
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
q: Union[str, None] = None
////
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
q: Annotated[Union[str, None]] = None
Cuando declaras otros parámetros de función que no son parte de los parámetros de path, son automáticamente interpretados como parámetros de "query".
-{* ../../docs_src/query_params/tutorial001.py hl[9] *}
+{* ../../docs_src/query_params/tutorial001_py39.py hl[9] *}
La query es el conjunto de pares clave-valor que van después del `?` en una URL, separados por caracteres `&`.
Pero cuando quieres hacer un parámetro de query requerido, simplemente no declares ningún valor por defecto:
-{* ../../docs_src/query_params/tutorial005.py hl[6:7] *}
+{* ../../docs_src/query_params/tutorial005_py39.py hl[6:7] *}
Aquí el parámetro de query `needy` es un parámetro de query requerido de tipo `str`.
El caso más común sería [devolver un Response directamente como se explica más adelante en la documentación avanzada](../advanced/response-directly.md){.internal-link target=_blank}.
-{* ../../docs_src/response_model/tutorial003_02.py hl[8,10:11] *}
+{* ../../docs_src/response_model/tutorial003_02_py39.py hl[8,10:11] *}
Este caso simple es manejado automáticamente por FastAPI porque la anotación del tipo de retorno es la clase (o una subclase de) `Response`.
También puedes usar una subclase de `Response` en la anotación del tipo:
-{* ../../docs_src/response_model/tutorial003_03.py hl[8:9] *}
+{* ../../docs_src/response_model/tutorial003_03_py39.py hl[8:9] *}
Esto también funcionará porque `RedirectResponse` es una subclase de `Response`, y FastAPI manejará automáticamente este caso simple.
* `@app.delete()`
* etc.
-{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
+{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
/// note | Nota
Veamos de nuevo el ejemplo anterior:
-{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
+{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
`201` es el código de estado para "Created".
Puedes usar las variables de conveniencia de `fastapi.status`.
-{* ../../docs_src/response_status_code/tutorial002.py hl[1,6] *}
+{* ../../docs_src/response_status_code/tutorial002_py39.py hl[1,6] *}
Son solo una conveniencia, mantienen el mismo número, pero de esa manera puedes usar el autocompletado del editor para encontrarlos:
* Importa `StaticFiles`.
* "Monta" una instance de `StaticFiles()` en un path específico.
-{* ../../docs_src/static_files/tutorial001.py hl[2,6] *}
+{* ../../docs_src/static_files/tutorial001_py39.py hl[2,6] *}
/// note | Detalles Técnicos
Escribe declaraciones `assert` simples con las expresiones estándar de Python que necesites revisar (otra vez, estándar de `pytest`).
-{* ../../docs_src/app_testing/tutorial001.py hl[2,12,15:18] *}
+{* ../../docs_src/app_testing/tutorial001_py39.py hl[2,12,15:18] *}
/// tip | Consejo
En el archivo `main.py` tienes tu aplicación de **FastAPI**:
-{* ../../docs_src/app_testing/main.py *}
+{* ../../docs_src/app_testing/app_a_py39/main.py *}
### Archivo de prueba { #testing-file }
Debido a que este archivo está en el mismo paquete, puedes usar importaciones relativas para importar el objeto `app` desde el módulo `main` (`main.py`):
-{* ../../docs_src/app_testing/test_main.py hl[3] *}
+{* ../../docs_src/app_testing/app_a_py39/test_main.py hl[3] *}
...y tener el código para las pruebas tal como antes.
Por exemplo, para declarar um outro retorno com o status code `404` e um modelo do Pydantic chamado `Message`, você pode escrever:
-{* ../../docs_src/additional_responses/tutorial001.py hl[18,22] *}
+{* ../../docs_src/additional_responses/tutorial001_py39.py hl[18,22] *}
/// note | Nota
E um retorno com o código de status `200` que utiliza o seu `response_model`, porém inclui um `example` customizado:
-{* ../../docs_src/additional_responses/tutorial003.py hl[20:31] *}
+{* ../../docs_src/additional_responses/tutorial003_py39.py hl[20:31] *}
Isso será combinado e incluído em seu OpenAPI, e disponibilizado na documentação da sua API:
O arquivo `main.py` teria:
-{* ../../docs_src/async_tests/main.py *}
+{* ../../docs_src/async_tests/app_a_py39/main.py *}
O arquivo `test_main.py` teria os testes para para o arquivo `main.py`, ele poderia ficar assim:
-{* ../../docs_src/async_tests/test_main.py *}
+{* ../../docs_src/async_tests/app_a_py39/test_main.py *}
## Executá-lo { #run-it }
O marcador `@pytest.mark.anyio` informa ao pytest que esta função de teste deve ser invocada de maneira assíncrona:
-{* ../../docs_src/async_tests/test_main.py hl[7] *}
+{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[7] *}
/// tip | Dica
Então podemos criar um `AsyncClient` com a aplicação, e enviar requisições assíncronas para ela utilizando `await`.
-{* ../../docs_src/async_tests/test_main.py hl[9:12] *}
+{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[9:12] *}
Isso é equivalente a:
Por exemplo, suponha que você defina uma *operação de rota* `/items/`:
-{* ../../docs_src/behind_a_proxy/tutorial001_01.py hl[6] *}
+{* ../../docs_src/behind_a_proxy/tutorial001_01_py39.py hl[6] *}
Se o cliente tentar ir para `/items`, por padrão, ele seria redirecionado para `/items/`.
Embora todo o seu código esteja escrito assumindo que existe apenas `/app`.
-{* ../../docs_src/behind_a_proxy/tutorial001.py hl[6] *}
+{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[6] *}
E o proxy estaria **"removendo"** o **prefixo de path** dinamicamente antes de transmitir a solicitação para o servidor da aplicação (provavelmente Uvicorn via CLI do FastAPI), mantendo sua aplicação convencida de que está sendo servida em `/app`, para que você não precise atualizar todo o seu código para incluir o prefixo `/api/v1`.
Aqui estamos incluindo-o na mensagem apenas para fins de demonstração.
-{* ../../docs_src/behind_a_proxy/tutorial001.py hl[8] *}
+{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[8] *}
Então, se você iniciar o Uvicorn com:
Alternativamente, se você não tiver uma maneira de fornecer uma opção de linha de comando como `--root-path` ou equivalente, você pode definir o parâmetro `root_path` ao criar sua aplicação FastAPI:
-{* ../../docs_src/behind_a_proxy/tutorial002.py hl[3] *}
+{* ../../docs_src/behind_a_proxy/tutorial002_py39.py hl[3] *}
Passar o `root_path` para `FastAPI` seria o equivalente a passar a opção de linha de comando `--root-path` para Uvicorn ou Hypercorn.
Por exemplo:
-{* ../../docs_src/behind_a_proxy/tutorial003.py hl[4:7] *}
+{* ../../docs_src/behind_a_proxy/tutorial003_py39.py hl[4:7] *}
Gerará um OpenAPI schema como:
Se você não quiser que o **FastAPI** inclua um servidor automático usando o `root_path`, você pode usar o parâmetro `root_path_in_servers=False`:
-{* ../../docs_src/behind_a_proxy/tutorial004.py hl[9] *}
+{* ../../docs_src/behind_a_proxy/tutorial004_py39.py hl[9] *}
e então ele não será incluído no OpenAPI schema.
Mas se você tem certeza que o conteúdo que você está retornando é **serializável com JSON**, você pode passá-lo diretamente para a classe de resposta e evitar o trabalho extra que o FastAPI teria ao passar o conteúdo pelo `jsonable_encoder` antes de passar para a classe de resposta.
-{* ../../docs_src/custom_response/tutorial001b.py hl[2,7] *}
+{* ../../docs_src/custom_response/tutorial001b_py39.py hl[2,7] *}
/// info | Informação
* Importe `HTMLResponse`
* Passe `HTMLResponse` como o parâmetro de `response_class` do seu *decorador de operação de rota*.
-{* ../../docs_src/custom_response/tutorial002.py hl[2,7] *}
+{* ../../docs_src/custom_response/tutorial002_py39.py hl[2,7] *}
/// info | Informação
O mesmo exemplo de antes, retornando uma `HTMLResponse`, poderia parecer com:
-{* ../../docs_src/custom_response/tutorial003.py hl[2,7,19] *}
+{* ../../docs_src/custom_response/tutorial003_py39.py hl[2,7,19] *}
/// warning | Atenção
Por exemplo, poderia ser algo como:
-{* ../../docs_src/custom_response/tutorial004.py hl[7,21,23] *}
+{* ../../docs_src/custom_response/tutorial004_py39.py hl[7,21,23] *}
Neste exemplo, a função `generate_html_response()` já cria e retorna uma `Response` em vez de retornar o HTML em uma `str`.
O FastAPI (Starlette, na verdade) irá incluir o cabeçalho Content-Length automaticamente. Ele também irá incluir o cabeçalho Content-Type, baseado no `media_type` e acrescentando uma codificação para tipos textuais.
-{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
+{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
### `HTMLResponse` { #htmlresponse }
Usa algum texto ou sequência de bytes para retornar uma resposta de texto não formatado.
-{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *}
+{* ../../docs_src/custom_response/tutorial005_py39.py hl[2,7,9] *}
### `JSONResponse` { #jsonresponse }
///
-{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *}
+{* ../../docs_src/custom_response/tutorial001_py39.py hl[2,7] *}
/// tip | Dica
Você pode retornar uma `RedirectResponse` diretamente:
-{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *}
+{* ../../docs_src/custom_response/tutorial006_py39.py hl[2,9] *}
---
Ou você pode utilizá-la no parâmetro `response_class`:
-{* ../../docs_src/custom_response/tutorial006b.py hl[2,7,9] *}
+{* ../../docs_src/custom_response/tutorial006b_py39.py hl[2,7,9] *}
Se você fizer isso, então você pode retornar a URL diretamente da sua *função de operação de rota*
Você também pode utilizar o parâmetro `status_code` combinado com o parâmetro `response_class`:
-{* ../../docs_src/custom_response/tutorial006c.py hl[2,7,9] *}
+{* ../../docs_src/custom_response/tutorial006c_py39.py hl[2,7,9] *}
### `StreamingResponse` { #streamingresponse }
Recebe um gerador assíncrono ou um gerador/iterador comum e retorna o corpo da resposta de forma contínua (stream).
-{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *}
+{* ../../docs_src/custom_response/tutorial007_py39.py hl[2,14] *}
#### Utilizando `StreamingResponse` com objetos semelhantes a arquivos { #using-streamingresponse-with-file-like-objects }
Isso inclui muitas bibliotecas que interagem com armazenamento em nuvem, processamento de vídeos, entre outras.
-{* ../../docs_src/custom_response/tutorial008.py hl[2,10:12,14] *}
+{* ../../docs_src/custom_response/tutorial008_py39.py hl[2,10:12,14] *}
1. Essa é a função geradora. É definida como "função geradora" porque contém declarações `yield` nela.
2. Ao utilizar o bloco `with`, nós garantimos que o objeto semelhante a um arquivo é fechado após a função geradora ser finalizada. Isto é, após a resposta terminar de ser enviada.
Respostas de Arquivos incluem o tamanho do arquivo, data da última modificação e ETags apropriados, nos cabeçalhos `Content-Length`, `Last-Modified` e `ETag`, respectivamente.
-{* ../../docs_src/custom_response/tutorial009.py hl[2,10] *}
+{* ../../docs_src/custom_response/tutorial009_py39.py hl[2,10] *}
Você também pode usar o parâmetro `response_class`:
-{* ../../docs_src/custom_response/tutorial009b.py hl[2,8,10] *}
+{* ../../docs_src/custom_response/tutorial009b_py39.py hl[2,8,10] *}
Nesse caso, você pode retornar o caminho do arquivo diretamente da sua *função de operação de rota*.
Você poderia criar uma classe `CustomORJSONResponse`. A principal coisa a ser feita é sobrecarregar o método render da classe Response, `Response.render(content)`, que retorna o conteúdo em bytes:
-{* ../../docs_src/custom_response/tutorial009c.py hl[9:14,17] *}
+{* ../../docs_src/custom_response/tutorial009c_py39.py hl[9:14,17] *}
Agora em vez de retornar:
No exemplo abaixo, o **FastAPI** irá utilizar `ORJSONResponse` por padrão, em todas as *operações de rota*, em vez de `JSONResponse`.
-{* ../../docs_src/custom_response/tutorial010.py hl[2,4] *}
+{* ../../docs_src/custom_response/tutorial010_py39.py hl[2,4] *}
/// tip | Dica
Nós criamos uma função assíncrona `lifespan()` com `yield` assim:
-{* ../../docs_src/events/tutorial003.py hl[16,19] *}
+{* ../../docs_src/events/tutorial003_py39.py hl[16,19] *}
Aqui estamos simulando a operação de *inicialização* custosa de carregar o modelo, colocando a (falsa) função do modelo no dicionário com modelos de machine learning antes do `yield`. Esse código será executado **antes** de a aplicação **começar a receber requisições**, durante a *inicialização*.
A primeira coisa a notar é que estamos definindo uma função assíncrona com `yield`. Isso é muito semelhante a Dependências com `yield`.
-{* ../../docs_src/events/tutorial003.py hl[14:19] *}
+{* ../../docs_src/events/tutorial003_py39.py hl[14:19] *}
A primeira parte da função, antes do `yield`, será executada **antes** de a aplicação iniciar.
Isso converte a função em algo chamado "**gerenciador de contexto assíncrono**".
-{* ../../docs_src/events/tutorial003.py hl[1,13] *}
+{* ../../docs_src/events/tutorial003_py39.py hl[1,13] *}
Um **gerenciador de contexto** em Python é algo que você pode usar em uma declaração `with`, por exemplo, `open()` pode ser usado como um gerenciador de contexto:
O parâmetro `lifespan` da aplicação `FastAPI` aceita um **gerenciador de contexto assíncrono**, então podemos passar para ele nosso novo gerenciador de contexto assíncrono `lifespan`.
-{* ../../docs_src/events/tutorial003.py hl[22] *}
+{* ../../docs_src/events/tutorial003_py39.py hl[22] *}
## Eventos alternativos (descontinuados) { #alternative-events-deprecated }
Para adicionar uma função que deve rodar antes de a aplicação iniciar, declare-a com o evento `"startup"`:
-{* ../../docs_src/events/tutorial001.py hl[8] *}
+{* ../../docs_src/events/tutorial001_py39.py hl[8] *}
Nesse caso, a função de manipulador do evento `startup` inicializará os itens do "banco de dados" (apenas um `dict`) com alguns valores.
Para adicionar uma função que deve ser executada quando a aplicação estiver encerrando, declare-a com o evento `"shutdown"`:
-{* ../../docs_src/events/tutorial002.py hl[6] *}
+{* ../../docs_src/events/tutorial002_py39.py hl[6] *}
Aqui, a função de manipulador do evento `shutdown` escreverá uma linha de texto `"Application shutdown"` no arquivo `log.txt`.
Poderíamos baixar o JSON do OpenAPI para um arquivo `openapi.json` e então poderíamos **remover essa tag prefixada** com um script como este:
-{* ../../docs_src/generate_clients/tutorial004.py *}
+{* ../../docs_src/generate_clients/tutorial004_py39.py *}
//// tab | Node.js
Qualquer requisição para `http` ou `ws` será redirecionada para o esquema seguro.
-{* ../../docs_src/advanced_middleware/tutorial001.py hl[2,6] *}
+{* ../../docs_src/advanced_middleware/tutorial001_py39.py hl[2,6] *}
## `TrustedHostMiddleware` { #trustedhostmiddleware }
Garante que todas as requisições recebidas tenham um cabeçalho `Host` corretamente configurado, a fim de proteger contra ataques de cabeçalho de host HTTP.
-{* ../../docs_src/advanced_middleware/tutorial002.py hl[2,6:8] *}
+{* ../../docs_src/advanced_middleware/tutorial002_py39.py hl[2,6:8] *}
Os seguintes argumentos são suportados:
O middleware lidará com respostas padrão e de streaming.
-{* ../../docs_src/advanced_middleware/tutorial003.py hl[2,6] *}
+{* ../../docs_src/advanced_middleware/tutorial003_py39.py hl[2,6] *}
Os seguintes argumentos são suportados:
Quando você cria uma aplicação com o **FastAPI**, existe um atributo chamado `webhooks`, que você utilizar para defini-los da mesma maneira que você definiria as suas **operações de rotas**, utilizando por exemplo `@app.webhooks.post()`.
-{* ../../docs_src/openapi_webhooks/tutorial001.py hl[9:13,36:53] *}
+{* ../../docs_src/openapi_webhooks/tutorial001_py39.py hl[9:13,36:53] *}
Os webhooks que você define aparecerão no esquema do **OpenAPI** e na **página de documentação** gerada automaticamente.
Você precisa ter certeza que ele é único para cada operação.
-{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *}
### Utilizando o nome da *função de operação de rota* como o operationId { #using-the-path-operation-function-name-as-the-operationid }
Você deve fazer isso depois de adicionar todas as suas *operações de rota*.
-{* ../../docs_src/path_operation_advanced_configuration/tutorial002.py hl[2, 12:21, 24] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py39.py hl[2, 12:21, 24] *}
/// tip | Dica
Para excluir uma *operação de rota* do esquema OpenAPI gerado (e por consequência, dos sistemas de documentação automáticos), utilize o parâmetro `include_in_schema` e defina ele como `False`:
-{* ../../docs_src/path_operation_advanced_configuration/tutorial003.py hl[6] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py39.py hl[6] *}
## Descrição avançada a partir de docstring { #advanced-description-from-docstring }
Esse parâmetro `openapi_extra` pode ser útil, por exemplo, para declarar [Extensões do OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
-{* ../../docs_src/path_operation_advanced_configuration/tutorial005.py hl[6] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py39.py hl[6] *}
Se você abrir os documentos criados automaticamente para a API, sua extensão aparecerá no final da *operação de rota* específica.
Você pode fazer isso com `openapi_extra`:
-{* ../../docs_src/path_operation_advanced_configuration/tutorial006.py hl[19:36, 39:40] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *}
Nesse exemplo, nós não declaramos nenhum modelo do Pydantic. Na verdade, o corpo da requisição não está nem mesmo <abbr title="convertido de um formato plano, como bytes, para objetos Python">analisado</abbr> como JSON, ele é lido diretamente como `bytes` e a função `magic_data_reader()` seria a responsável por analisar ele de alguma forma.
E então você pode definir o `status_code` neste objeto de retorno temporal.
-{* ../../docs_src/response_change_status_code/tutorial001.py hl[1,9,12] *}
+{* ../../docs_src/response_change_status_code/tutorial001_py39.py hl[1,9,12] *}
E então você pode retornar qualquer objeto que você precise, como você faria normalmente (um `dict`, um modelo de banco de dados, etc.).
E então você pode definir cookies nesse objeto de resposta *temporário*.
-{* ../../docs_src/response_cookies/tutorial002.py hl[1, 8:9] *}
+{* ../../docs_src/response_cookies/tutorial002_py39.py hl[1, 8:9] *}
Em seguida, você pode retornar qualquer objeto que precise, como normalmente faria (um `dict`, um modelo de banco de dados, etc).
Então, defina os cookies nela e a retorne:
-{* ../../docs_src/response_cookies/tutorial001.py hl[10:12] *}
+{* ../../docs_src/response_cookies/tutorial001_py39.py hl[10:12] *}
/// tip | Dica
Você pode colocar o seu conteúdo XML em uma string, colocar em uma `Response`, e retorná-lo:
-{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
+{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
## Notas { #notes }
Então você pode definir os cabeçalhos nesse objeto de resposta *temporário*.
-{* ../../docs_src/response_headers/tutorial002.py hl[1, 7:8] *}
+{* ../../docs_src/response_headers/tutorial002_py39.py hl[1, 7:8] *}
Em seguida você pode retornar qualquer objeto que precisar, da maneira que faria normalmente (um `dict`, um modelo de banco de dados, etc.).
Crie uma resposta conforme descrito em [Retornar uma resposta diretamente](response-directly.md){.internal-link target=_blank} e passe os cabeçalhos como um parâmetro adicional:
-{* ../../docs_src/response_headers/tutorial001.py hl[10:12] *}
+{* ../../docs_src/response_headers/tutorial001_py39.py hl[10:12] *}
/// note | Detalhes Técnicos
//// tab | Pydantic v2
-{* ../../docs_src/settings/tutorial001.py hl[2,5:8,11] *}
+{* ../../docs_src/settings/tutorial001_py39.py hl[2,5:8,11] *}
////
///
-{* ../../docs_src/settings/tutorial001_pv1.py hl[2,5:8,11] *}
+{* ../../docs_src/settings/tutorial001_pv1_py39.py hl[2,5:8,11] *}
////
Depois você pode usar o novo objeto `settings` na sua aplicação:
-{* ../../docs_src/settings/tutorial001.py hl[18:20] *}
+{* ../../docs_src/settings/tutorial001_py39.py hl[18:20] *}
### Executar o servidor { #run-the-server }
Por exemplo, você poderia ter um arquivo `config.py` com:
-{* ../../docs_src/settings/app01/config.py *}
+{* ../../docs_src/settings/app01_py39/config.py *}
E então usá-lo em um arquivo `main.py`:
-{* ../../docs_src/settings/app01/main.py hl[3,11:13] *}
+{* ../../docs_src/settings/app01_py39/main.py hl[3,11:13] *}
/// tip | Dica
Primeiro, crie a aplicação principal, de nível superior, **FastAPI**, e suas *operações de rota*:
-{* ../../docs_src/sub_applications/tutorial001.py hl[3, 6:8] *}
+{* ../../docs_src/sub_applications/tutorial001_py39.py hl[3, 6:8] *}
### Sub-aplicação { #sub-application }
Essa sub-aplicação é apenas outra aplicação FastAPI padrão, mas esta é a que será "montada":
-{* ../../docs_src/sub_applications/tutorial001.py hl[11, 14:16] *}
+{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 14:16] *}
### Monte a sub-aplicação { #mount-the-sub-application }
Neste caso, ela será montada no path `/subapi`:
-{* ../../docs_src/sub_applications/tutorial001.py hl[11, 19] *}
+{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 19] *}
### Verifique a documentação automática da API { #check-the-automatic-api-docs }
* Declare um parâmetro `Request` no *path operation* que retornará um template.
* Use o `templates` que você criou para renderizar e retornar uma `TemplateResponse`, passe o nome do template, o objeto `request` e um dicionário "context" com pares chave-valor a serem usados dentro do template do Jinja2.
-{* ../../docs_src/templates/tutorial001.py hl[4,11,15:18] *}
+{* ../../docs_src/templates/tutorial001_py39.py hl[4,11,15:18] *}
/// note | Nota
Quando você precisa que o `lifespan` seja executado em seus testes, você pode utilizar o `TestClient` com a instrução `with`:
-{* ../../docs_src/app_testing/tutorial004.py hl[9:15,18,27:28,30:32,41:43] *}
+{* ../../docs_src/app_testing/tutorial004_py39.py hl[9:15,18,27:28,30:32,41:43] *}
Você pode ler mais detalhes sobre o ["Executando lifespan em testes no site oficial da documentação do Starlette."](https://www.starlette.dev/lifespan/#running-lifespan-in-tests)
Para os eventos `startup` e `shutdown` descontinuados, você pode usar o `TestClient` da seguinte forma:
-{* ../../docs_src/app_testing/tutorial003.py hl[9:12,20:24] *}
+{* ../../docs_src/app_testing/tutorial003_py39.py hl[9:12,20:24] *}
Para isso, você utiliza o `TestClient` dentro de uma instrução `with`, conectando com o WebSocket:
-{* ../../docs_src/app_testing/tutorial002.py hl[27:31] *}
+{* ../../docs_src/app_testing/tutorial002_py39.py hl[27:31] *}
/// note | Nota
Para isso você precisa acessar a requisição diretamente.
-{* ../../docs_src/using_request_directly/tutorial001.py hl[1,7:8] *}
+{* ../../docs_src/using_request_directly/tutorial001_py39.py hl[1,7:8] *}
Ao declarar o parâmetro com o tipo sendo um `Request` em sua *função de operação de rota*, o **FastAPI** saberá como passar o `Request` neste parâmetro.
Mas é a maneira mais simples de focar no lado do servidor de WebSockets e ter um exemplo funcional:
-{* ../../docs_src/websockets/tutorial001.py hl[2,6:38,41:43] *}
+{* ../../docs_src/websockets/tutorial001_py39.py hl[2,6:38,41:43] *}
## Crie um `websocket` { #create-a-websocket }
Em sua aplicação **FastAPI**, crie um `websocket`:
-{* ../../docs_src/websockets/tutorial001.py hl[1,46:47] *}
+{* ../../docs_src/websockets/tutorial001_py39.py hl[1,46:47] *}
/// note | Detalhes Técnicos
Em sua rota WebSocket você pode esperar (`await`) por mensagens e enviar mensagens.
-{* ../../docs_src/websockets/tutorial001.py hl[48:52] *}
+{* ../../docs_src/websockets/tutorial001_py39.py hl[48:52] *}
Você pode receber e enviar dados binários, de texto e JSON.
E então monte isso sob um path.
-{* ../../docs_src/wsgi/tutorial001.py hl[2:3,3] *}
+{* ../../docs_src/wsgi/tutorial001_py39.py hl[2:3,3] *}
## Confira { #check-it }
Por exemplo:
-{* ../../docs_src/conditional_openapi/tutorial001.py hl[6,11] *}
+{* ../../docs_src/conditional_openapi/tutorial001_py39.py hl[6,11] *}
Aqui declaramos a configuração `openapi_url` com o mesmo padrão de `"/openapi.json"`.
Mas você pode desabilitá-lo definindo `syntaxHighlight` como `False`:
-{* ../../docs_src/configure_swagger_ui/tutorial001.py hl[3] *}
+{* ../../docs_src/configure_swagger_ui/tutorial001_py39.py hl[3] *}
...e então o Swagger UI não mostrará mais o destaque de sintaxe:
Da mesma forma que você pode definir o tema de destaque de sintaxe com a chave `"syntaxHighlight.theme"` (observe que há um ponto no meio):
-{* ../../docs_src/configure_swagger_ui/tutorial002.py hl[3] *}
+{* ../../docs_src/configure_swagger_ui/tutorial002_py39.py hl[3] *}
Essa configuração alteraria o tema de cores de destaque de sintaxe:
Por exemplo, para desabilitar `deepLinking` você pode passar essas configurações para `swagger_ui_parameters`:
-{* ../../docs_src/configure_swagger_ui/tutorial003.py hl[3] *}
+{* ../../docs_src/configure_swagger_ui/tutorial003_py39.py hl[3] *}
## Outros parâmetros da UI do Swagger { #other-swagger-ui-parameters }
Para desativá-los, defina suas URLs como `None` ao criar sua aplicação FastAPI:
-{* ../../docs_src/custom_docs_ui/tutorial001.py hl[8] *}
+{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[8] *}
### Incluir a documentação personalizada { #include-the-custom-docs }
E de forma semelhante para o ReDoc...
-{* ../../docs_src/custom_docs_ui/tutorial001.py hl[2:6,11:19,22:24,27:33] *}
+{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[2:6,11:19,22:24,27:33] *}
/// tip | Dica
Agora, para poder testar se tudo funciona, crie uma *operação de rota*:
-{* ../../docs_src/custom_docs_ui/tutorial001.py hl[36:38] *}
+{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[36:38] *}
### Teste { #test-it }
* Importe `StaticFiles`.
* "Monte" a instância `StaticFiles()` em um caminho específico.
-{* ../../docs_src/custom_docs_ui/tutorial002.py hl[7,11] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[7,11] *}
### Teste os arquivos estáticos { #test-the-static-files }
Para desativá-los, defina suas URLs como `None` ao criar sua aplicação FastAPI:
-{* ../../docs_src/custom_docs_ui/tutorial002.py hl[9] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[9] *}
### Incluir a documentação personalizada para arquivos estáticos { #include-the-custom-docs-for-static-files }
E de forma semelhante para o ReDoc...
-{* ../../docs_src/custom_docs_ui/tutorial002.py hl[2:6,14:22,25:27,30:36] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[2:6,14:22,25:27,30:36] *}
/// tip | Dica
Agora, para poder testar se tudo funciona, crie uma *operação de rota*:
-{* ../../docs_src/custom_docs_ui/tutorial002.py hl[39:41] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[39:41] *}
### Teste a UI de Arquivos Estáticos { #test-static-files-ui }
Primeiro, escreva toda a sua aplicação **FastAPI** normalmente:
-{* ../../docs_src/extending_openapi/tutorial001.py hl[1,4,7:9] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[1,4,7:9] *}
### Gerar o esquema OpenAPI { #generate-the-openapi-schema }
Em seguida, use a mesma função utilitária para gerar o esquema OpenAPI, dentro de uma função `custom_openapi()`:
-{* ../../docs_src/extending_openapi/tutorial001.py hl[2,15:21] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[2,15:21] *}
### Modificar o esquema OpenAPI { #modify-the-openapi-schema }
Agora, você pode adicionar a extensão do ReDoc, incluindo um `x-logo` personalizado ao "objeto" `info` no esquema OpenAPI:
-{* ../../docs_src/extending_openapi/tutorial001.py hl[22:24] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[22:24] *}
### Armazenar em cache o esquema OpenAPI { #cache-the-openapi-schema }
Ele será gerado apenas uma vez, e o mesmo esquema armazenado em cache será utilizado nas próximas requisições.
-{* ../../docs_src/extending_openapi/tutorial001.py hl[13:14,25:26] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[13:14,25:26] *}
### Sobrescrever o método { #override-the-method }
Agora, você pode substituir o método `.openapi()` pela sua nova função.
-{* ../../docs_src/extending_openapi/tutorial001.py hl[29] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[29] *}
### Verificar { #check-it }
Aqui está uma pequena prévia de como você poderia integrar Strawberry com FastAPI:
-{* ../../docs_src/graphql/tutorial001.py hl[3,22,25] *}
+{* ../../docs_src/graphql/tutorial001_py39.py hl[3,22,25] *}
Você pode aprender mais sobre Strawberry na <a href="https://strawberry.rocks/" class="external-link" target="_blank">documentação do Strawberry</a>.
Vamos começar com um exemplo simples:
-{* ../../docs_src/python_types/tutorial001.py *}
+{* ../../docs_src/python_types/tutorial001_py39.py *}
A chamada deste programa gera:
* Converte a primeira letra de cada uma em maiúsculas com `title()`.
* <abbr title = "Agrupa-os, como um. Com o conteúdo de um após o outro.">Concatena</abbr> com um espaço no meio.
-{* ../../docs_src/python_types/tutorial001.py hl[2] *}
+{* ../../docs_src/python_types/tutorial001_py39.py hl[2] *}
### Edite-o { #edit-it }
Esses são os "type hints":
-{* ../../docs_src/python_types/tutorial002.py hl[1] *}
+{* ../../docs_src/python_types/tutorial002_py39.py hl[1] *}
Isso não é o mesmo que declarar valores padrão como seria com:
Verifique esta função, ela já possui type hints:
-{* ../../docs_src/python_types/tutorial003.py hl[1] *}
+{* ../../docs_src/python_types/tutorial003_py39.py hl[1] *}
Como o editor conhece os tipos de variáveis, você não obtém apenas o preenchimento automático, mas também as verificações de erro:
Agora você sabe que precisa corrigí-lo, converta `age` em uma string com `str(age)`:
-{* ../../docs_src/python_types/tutorial004.py hl[2] *}
+{* ../../docs_src/python_types/tutorial004_py39.py hl[2] *}
## Declarando Tipos { #declaring-types }
* `bool`
* `bytes`
-{* ../../docs_src/python_types/tutorial005.py hl[1] *}
+{* ../../docs_src/python_types/tutorial005_py39.py hl[1] *}
### Tipos genéricos com parâmetros de tipo { #generic-types-with-type-parameters }
Por exemplo, vamos definir uma variável para ser uma `list` de `str`.
-//// tab | Python 3.9+
-
-Declare uma variável com a mesma sintaxe com dois pontos (`:`)
-
-Como tipo, coloque `list`.
-
-Como a lista é o tipo que contém algum tipo interno, você coloca o tipo dentro de colchetes:
-
-```Python hl_lines="1"
-{!> ../../docs_src/python_types/tutorial006_py39.py!}
-```
-
-////
+Declare a variável, com a mesma sintaxe com dois pontos (`:`).
-//// tab | Python 3.8+
+Como o tipo, coloque `list`.
-De `typing`, importe `List` (com o `L` maiúsculo):
-
-```Python hl_lines="1"
-{!> ../../docs_src/python_types/tutorial006.py!}
-```
+Como a lista é um tipo que contém tipos internos, você os coloca entre colchetes:
-Declare uma variável com a mesma sintaxe com dois pontos (`:`)
-
-Como tipo, coloque o `List` que você importou de `typing`.
-
-Como a lista é o tipo que contém algum tipo interno, você coloca o tipo dentro de colchetes:
-
-```Python hl_lines="4"
-{!> ../../docs_src/python_types/tutorial006.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial006_py39.py hl[1] *}
/// info | Informação
Estes tipos internos dentro dos colchetes são chamados "parâmetros de tipo" (type parameters).
-Neste caso, `str` é o parâmetro de tipo passado para `List` (ou `list` no Python 3.9 ou superior).
+Neste caso, `str` é o parâmetro de tipo passado para `list`.
///
Isso significa: "a variável `items` é uma `list`, e cada um dos itens desta lista é uma `str`".
-/// tip | Dica
-
-Se você usa o Python 3.9 ou superior, você não precisa importar `List` de `typing`. Você pode utilizar o mesmo tipo `list` no lugar.
-
-///
-
Ao fazer isso, seu editor pode fornecer suporte mesmo durante o processamento de itens da lista:
<img src="/img/python-types/image05.png">
Você faria o mesmo para declarar `tuple`s e `set`s:
-//// tab | Python 3.9+
-
-```Python hl_lines="1"
-{!> ../../docs_src/python_types/tutorial007_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial007.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial007_py39.py hl[1] *}
Isso significa que:
O segundo parâmetro de tipo é para os valores do `dict`:
-//// tab | Python 3.9+
-
-```Python hl_lines="1"
-{!> ../../docs_src/python_types/tutorial008_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial008.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial008_py39.py hl[1] *}
Isso significa que:
////
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial008b.py!}
+{!> ../../docs_src/python_types/tutorial008b_py39.py!}
```
////
No Python 3.6 e superior (incluindo o Python 3.10) você pode declará-lo importando e utilizando `Optional` do módulo `typing`.
```Python hl_lines="1 4"
-{!../../docs_src/python_types/tutorial009.py!}
+{!../../docs_src/python_types/tutorial009_py39.py!}
```
O uso de `Optional[str]` em vez de apenas `str` permitirá que o editor o ajude a detectar erros, onde você pode estar assumindo que um valor é sempre um `str`, quando na verdade também pode ser `None`.
////
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial009.py!}
+{!> ../../docs_src/python_types/tutorial009_py39.py!}
```
////
-//// tab | Python 3.8+ alternativa
+//// tab | Python 3.9+ alternativa
```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial009b.py!}
+{!> ../../docs_src/python_types/tutorial009b_py39.py!}
```
////
Por exemplo, vamos pegar esta função:
-{* ../../docs_src/python_types/tutorial009c.py hl[1,4] *}
+{* ../../docs_src/python_types/tutorial009c_py39.py hl[1,4] *}
O parâmetro `name` é definido como `Optional[str]`, mas ele **não é opcional**, você não pode chamar a função sem o parâmetro:
* `set`
* `dict`
-E o mesmo como no Python 3.8, do módulo `typing`:
+E o mesmo que com versões anteriores do Python, do módulo `typing`:
* `Union`
-* `Optional` (o mesmo que com o 3.8)
+* `Optional`
* ...entre outros.
No Python 3.10, como uma alternativa para a utilização dos genéricos `Union` e `Optional`, você pode usar a <abbr title='também chamado de "bitwise ou operador", mas o significado não é relevante aqui'>barra vertical (`|`)</abbr> para declarar uniões de tipos. Isso é muito melhor e mais simples.
* `set`
* `dict`
-E o mesmo como no Python 3.8, do módulo `typing`:
-
-* `Union`
-* `Optional`
-* ...entre outros.
-
-////
-
-//// tab | Python 3.8+
+E genéricos do módulo `typing`:
-* `List`
-* `Tuple`
-* `Set`
-* `Dict`
* `Union`
* `Optional`
* ...entre outros.
Digamos que você tenha uma classe `Person`, com um nome:
-{* ../../docs_src/python_types/tutorial010.py hl[1:3] *}
+{* ../../docs_src/python_types/tutorial010_py39.py hl[1:3] *}
Então você pode declarar que uma variável é do tipo `Person`:
-{* ../../docs_src/python_types/tutorial010.py hl[6] *}
+{* ../../docs_src/python_types/tutorial010_py39.py hl[6] *}
-E então, novamente, você recebe todo o suporte do editor:
+E então, novamente, você recebe todo o apoio do editor:
<img src="/img/python-types/image06.png">
E você recebe todo o suporte do editor com esse objeto resultante.
-Retirado dos documentos oficiais dos Pydantic:
+Um exemplo da documentação oficial do Pydantic:
-//// tab | Python 3.10+
-
-```Python
-{!> ../../docs_src/python_types/tutorial011_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python
-{!> ../../docs_src/python_types/tutorial011_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python
-{!> ../../docs_src/python_types/tutorial011.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial011_py310.py *}
/// info | Informação
O Python possui uma funcionalidade que nos permite incluir **<abbr title="Informação sobre a informação, neste caso, informação sobre o tipo, e.g. uma descrição.">metadados</abbr> adicionais** nos type hints utilizando `Annotated`.
-//// tab | Python 3.9+
-
-No Python 3.9, `Annotated` é parte da biblioteca padrão, então você pode importá-lo de `typing`.
-
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial013_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-Em versões abaixo do Python 3.9, você importa `Annotated` de `typing_extensions`.
+Desde o Python 3.9, `Annotated` faz parte da biblioteca padrão, então você pode importá-lo de `typing`.
-Ele já estará instalado com o **FastAPI**.
-
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial013.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial013_py39.py hl[1,4] *}
O Python em si não faz nada com este `Annotated`. E para editores e outras ferramentas, o tipo ainda é `str`.
Primeiro, importe `BackgroundTasks` e defina um parâmetro na sua *função de operação de rota* com uma declaração de tipo `BackgroundTasks`:
-{* ../../docs_src/background_tasks/tutorial001.py hl[1,13] *}
+{* ../../docs_src/background_tasks/tutorial001_py39.py hl[1,13] *}
O **FastAPI** criará o objeto do tipo `BackgroundTasks` para você e o passará como esse parâmetro.
E como a operação de escrita não usa `async` e `await`, definimos a função com um `def` normal:
-{* ../../docs_src/background_tasks/tutorial001.py hl[6:9] *}
+{* ../../docs_src/background_tasks/tutorial001_py39.py hl[6:9] *}
## Adicione a tarefa em segundo plano { #add-the-background-task }
Dentro da sua *função de operação de rota*, passe sua função de tarefa para o objeto de *tarefas em segundo plano* com o método `.add_task()`:
-{* ../../docs_src/background_tasks/tutorial001.py hl[14] *}
+{* ../../docs_src/background_tasks/tutorial001_py39.py hl[14] *}
O `.add_task()` recebe como argumentos:
Mas o Python tem uma maneira específica de declarar listas com tipos internos ou "parâmetros de tipo":
-### Importe `List` do typing { #import-typings-list }
-
-No Python 3.9 e superior você pode usar a `list` padrão para declarar essas anotações de tipo, como veremos abaixo. 💡
-
-Mas nas versões do Python anteriores à 3.9 (3.6 e superiores), primeiro é necessário importar `List` do módulo padrão `typing` do Python:
-
-{* ../../docs_src/body_nested_models/tutorial002.py hl[1] *}
-
### Declare uma `list` com um parâmetro de tipo { #declare-a-list-with-a-type-parameter }
-Para declarar tipos que têm parâmetros de tipo (tipos internos), como `list`, `dict`, `tuple`:
-
-* Se você estiver em uma versão do Python inferior a 3.9, importe a versão equivalente do módulo `typing`
-* Passe o(s) tipo(s) interno(s) como "parâmetros de tipo" usando colchetes: `[` e `]`
-
-No Python 3.9, seria:
+Para declarar tipos que têm parâmetros de tipo (tipos internos), como `list`, `dict`, `tuple`,
+passe o(s) tipo(s) interno(s) como "parâmetros de tipo" usando colchetes: `[` e `]`
```Python
my_list: list[str]
```
-Em versões do Python anteriores à 3.9, seria:
-
-```Python
-from typing import List
-
-my_list: List[str]
-```
-
Essa é a sintaxe padrão do Python para declarações de tipo.
Use a mesma sintaxe padrão para atributos de modelo com tipos internos.
Se o valor de primeiro nível do corpo JSON que você espera for um `array` do JSON (uma` lista` do Python), você pode declarar o tipo no parâmetro da função, da mesma forma que nos modelos do Pydantic:
-```Python
-images: List[Image]
-```
-
-ou no Python 3.9 e superior:
-
```Python
images: list[Image]
```
O FastAPI saberá que o valor de `q` não é obrigatório por causa do valor padrão `= None`.
-O `str | None` (Python 3.10+) ou o `Union` em `Union[str, None]` (Python 3.8+) não é utilizado pelo FastAPI para determinar que o valor não é obrigatório, ele saberá que não é obrigatório porque tem um valor padrão `= None`.
+O `str | None` (Python 3.10+) ou o `Union` em `Union[str, None]` (Python 3.9+) não é utilizado pelo FastAPI para determinar que o valor não é obrigatório, ele saberá que não é obrigatório porque tem um valor padrão `= None`.
Mas adicionar as anotações de tipo permitirá ao seu editor oferecer um suporte melhor e detectar erros.
* Métodos HTTP específicos (`POST`, `PUT`) ou todos eles com o curinga `"*"`.
* Cabeçalhos HTTP específicos ou todos eles com o curinga `"*"`.
-{* ../../docs_src/cors/tutorial001.py hl[2,6:11,13:19] *}
+{* ../../docs_src/cors/tutorial001_py39.py hl[2,6:11,13:19] *}
Os parâmetros padrão usados pela implementação `CORSMiddleware` são restritivos por padrão, então você precisará habilitar explicitamente as origens, métodos ou cabeçalhos específicos para que os navegadores tenham permissão para usá-los em um contexto cross domain.
Em sua aplicação FastAPI, importe e execute `uvicorn` diretamente:
-{* ../../docs_src/debugging/tutorial001.py hl[1,15] *}
+{* ../../docs_src/debugging/tutorial001_py39.py hl[1,15] *}
### Sobre `__name__ == "__main__"` { #about-name-main }
Perceba como escrevemos `CommonQueryParams` duas vezes no código abaixo:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
-//// tab | Python 3.8+ non-Annotated
+//// tab | Python 3.9+ non-Annotated
/// tip | Dica
Nesse caso, o primeiro `CommonQueryParams`, em:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, ...
////
-//// tab | Python 3.8+ non-Annotated
+//// tab | Python 3.9+ non-Annotated
/// tip | Dica
Na verdade você poderia escrever apenas:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[Any, Depends(CommonQueryParams)]
////
-//// tab | Python 3.8+ non-Annotated
+//// tab | Python 3.9+ non-Annotated
/// tip | Dica
Mas você pode ver que temos uma repetição do código neste exemplo, escrevendo `CommonQueryParams` duas vezes:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
-//// tab | Python 3.8+ non-Annotated
+//// tab | Python 3.9+ non-Annotated
/// tip | Dica
Em vez de escrever:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
-//// tab | Python 3.8+ non-Annotated
+//// tab | Python 3.9+ non-Annotated
/// tip | Dica
...escreva:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends()]
////
-//// tab | Python 3.8 non-Annotated
+//// tab | Python 3.9+ non-Annotated
/// tip | Dica
Apenas o código anterior à declaração com `yield` e o código contendo essa declaração são executados antes de criar uma resposta:
-{* ../../docs_src/dependencies/tutorial007.py hl[2:4] *}
+{* ../../docs_src/dependencies/tutorial007_py39.py hl[2:4] *}
O valor gerado (yielded) é o que é injetado nas *operações de rota* e outras dependências:
-{* ../../docs_src/dependencies/tutorial007.py hl[4] *}
+{* ../../docs_src/dependencies/tutorial007_py39.py hl[4] *}
O código após o `yield` é executado após a resposta:
-{* ../../docs_src/dependencies/tutorial007.py hl[5:6] *}
+{* ../../docs_src/dependencies/tutorial007_py39.py hl[5:6] *}
/// tip | Dica
Da mesma forma, você pode utilizar `finally` para garantir que os passos de saída são executados, com ou sem exceções.
-{* ../../docs_src/dependencies/tutorial007.py hl[3,5] *}
+{* ../../docs_src/dependencies/tutorial007_py39.py hl[3,5] *}
## Subdependências com `yield` { #sub-dependencies-with-yield }
Você também pode usá-los dentro de dependências com `yield` do **FastAPI** ao utilizar
`with` ou `async with` dentro da função da dependência:
-{* ../../docs_src/dependencies/tutorial010.py hl[1:9,13] *}
+{* ../../docs_src/dependencies/tutorial010_py39.py hl[1:9,13] *}
/// tip | Dica
Nesse caso, elas serão aplicadas a todas as *operações de rota* da aplicação:
-{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[16] *}
+{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[17] *}
E todos os conceitos apresentados na seção sobre [adicionar `dependencies` aos *decoradores de operação de rota*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} ainda se aplicam, mas nesse caso, para todas as *operações de rota* da aplicação.
Em um cenário avançado onde você precise que a dependência seja calculada em cada passo (possivelmente várias vezes) de uma requisição em vez de utilizar o valor em "cache", você pode definir o parâmetro `use_cache=False` em `Depends`:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python hl_lines="1"
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
////
-//// tab | Python 3.8+ non-Annotated
+//// tab | Python 3.9+ non-Annotated
/// tip | Dica
O arquivo FastAPI mais simples pode se parecer com:
-{* ../../docs_src/first_steps/tutorial001.py *}
+{* ../../docs_src/first_steps/tutorial001_py39.py *}
Copie o conteúdo para um arquivo `main.py`.
### Passo 1: importe `FastAPI` { #step-1-import-fastapi }
-{* ../../docs_src/first_steps/tutorial001.py hl[1] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[1] *}
`FastAPI` é uma classe Python que fornece todas as funcionalidades para sua API.
### Passo 2: crie uma "instância" de `FastAPI` { #step-2-create-a-fastapi-instance }
-{* ../../docs_src/first_steps/tutorial001.py hl[3] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[3] *}
Aqui, a variável `app` será uma "instância" da classe `FastAPI`.
#### Defina um decorador de operação de rota { #define-a-path-operation-decorator }
-{* ../../docs_src/first_steps/tutorial001.py hl[6] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[6] *}
O `@app.get("/")` diz ao **FastAPI** que a função logo abaixo é responsável por tratar as requisições que vão para:
* **operação**: é `get`.
* **função**: é a função abaixo do "decorador" (abaixo do `@app.get("/")`).
-{* ../../docs_src/first_steps/tutorial001.py hl[7] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[7] *}
Esta é uma função Python.
Você também pode defini-la como uma função normal em vez de `async def`:
-{* ../../docs_src/first_steps/tutorial003.py hl[7] *}
+{* ../../docs_src/first_steps/tutorial003_py39.py hl[7] *}
/// note | Nota
### Passo 5: retorne o conteúdo { #step-5-return-the-content }
-{* ../../docs_src/first_steps/tutorial001.py hl[8] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[8] *}
Você pode retornar um `dict`, `list` e valores singulares como `str`, `int`, etc.
### Import `HTTPException` { #import-httpexception }
-{* ../../docs_src/handling_errors/tutorial001.py hl[1] *}
+{* ../../docs_src/handling_errors/tutorial001_py39.py hl[1] *}
### Lance o `HTTPException` no seu código { #raise-an-httpexception-in-your-code }
Neste exemplo, quando o cliente pede, na requisição, por um item cujo ID não existe, a exceção com o status code `404` é lançada:
-{* ../../docs_src/handling_errors/tutorial001.py hl[11] *}
+{* ../../docs_src/handling_errors/tutorial001_py39.py hl[11] *}
### A response resultante { #the-resulting-response }
Mas caso você precise, para um cenário mais complexo, você pode adicionar headers customizados:
-{* ../../docs_src/handling_errors/tutorial002.py hl[14] *}
+{* ../../docs_src/handling_errors/tutorial002_py39.py hl[14] *}
## Instale manipuladores de exceções customizados { #install-custom-exception-handlers }
Nesse cenário, se você precisa manipular essa exceção de modo global com o FastAPI, você pode adicionar um manipulador de exceção customizada com `@app.exception_handler()`.
-{* ../../docs_src/handling_errors/tutorial003.py hl[5:7,13:18,24] *}
+{* ../../docs_src/handling_errors/tutorial003_py39.py hl[5:7,13:18,24] *}
Nesse cenário, se você fizer uma requisição para `/unicorns/yolo`, a *operação de caminho* vai lançar (`raise`) o `UnicornException`.
O manipulador de exceções receberá um `Request` e a exceção.
-{* ../../docs_src/handling_errors/tutorial004.py hl[2,14:19] *}
+{* ../../docs_src/handling_errors/tutorial004_py39.py hl[2,14:19] *}
Se você for ao `/items/foo`, em vez de receber o JSON padrão com o erro:
Por exemplo, você pode querer retornar uma *response* em *plain text* ao invés de um JSON para os seguintes erros:
-{* ../../docs_src/handling_errors/tutorial004.py hl[3:4,9:11,25] *}
+{* ../../docs_src/handling_errors/tutorial004_py39.py hl[3:4,9:11,25] *}
/// note | Detalhes Técnicos
Você pode utilizá-lo enquanto desenvolve seu app para registrar o *body* e debugá-lo, e assim retorná-lo ao usuário, etc.
-{* ../../docs_src/handling_errors/tutorial005.py hl[14] *}
+{* ../../docs_src/handling_errors/tutorial005_py39.py hl[14] *}
Tente enviar um item inválido como este:
Se você quer usar a exceção em conjunto com o mesmo manipulador de exceção *default* do **FastAPI**, você pode importar e re-usar esses manipuladores de exceção do `fastapi.exception_handlers`:
-{* ../../docs_src/handling_errors/tutorial006.py hl[2:5,15,21] *}
+{* ../../docs_src/handling_errors/tutorial006_py39.py hl[2:5,15,21] *}
Nesse exemplo você apenas imprime (`print`) o erro com uma mensagem expressiva. Mesmo assim, dá para pegar a ideia. Você pode usar a exceção e então apenas re-usar o manipulador de exceção *default*.
Você pode defini-los da seguinte maneira:
-{* ../../docs_src/metadata/tutorial001.py hl[3:16,19:32] *}
+{* ../../docs_src/metadata/tutorial001_py39.py hl[3:16, 19:32] *}
/// tip | Dica
Por exemplo:
-{* ../../docs_src/metadata/tutorial001_1.py hl[31] *}
+{* ../../docs_src/metadata/tutorial001_1_py39.py hl[31] *}
## Metadados para tags { #metadata-for-tags }
Crie metadados para suas tags e passe-os para o parâmetro `openapi_tags`:
-{* ../../docs_src/metadata/tutorial004.py hl[3:16,18] *}
+{* ../../docs_src/metadata/tutorial004_py39.py hl[3:16,18] *}
Observe que você pode usar Markdown dentro das descrições. Por exemplo, "login" será exibido em negrito (**login**) e "fancy" será exibido em itálico (_fancy_).
Use o parâmetro `tags` com suas *operações de rota* (e `APIRouter`s) para atribuí-los a diferentes tags:
-{* ../../docs_src/metadata/tutorial004.py hl[21,26] *}
+{* ../../docs_src/metadata/tutorial004_py39.py hl[21,26] *}
/// info | Informação
Por exemplo, para defini-lo para ser servido em `/api/v1/openapi.json`:
-{* ../../docs_src/metadata/tutorial002.py hl[3] *}
+{* ../../docs_src/metadata/tutorial002_py39.py hl[3] *}
Se você quiser desativar completamente o esquema OpenAPI, pode definir `openapi_url=None`, o que também desativará as interfaces de documentação que o utilizam.
Por exemplo, para definir o Swagger UI para ser servido em `/documentation` e desativar o ReDoc:
-{* ../../docs_src/metadata/tutorial003.py hl[3] *}
+{* ../../docs_src/metadata/tutorial003_py39.py hl[3] *}
* Então ela retorna a `response` gerada pela *operação de rota* correspondente.
* Você pode então modificar ainda mais o `response` antes de retorná-lo.
-{* ../../docs_src/middleware/tutorial001.py hl[8:9,11,14] *}
+{* ../../docs_src/middleware/tutorial001_py39.py hl[8:9,11,14] *}
/// tip | Dica
Por exemplo, você pode adicionar um cabeçalho personalizado `X-Process-Time` contendo o tempo em segundos que levou para processar a solicitação e gerar uma resposta:
-{* ../../docs_src/middleware/tutorial001.py hl[10,12:13] *}
+{* ../../docs_src/middleware/tutorial001_py39.py hl[10,12:13] *}
/// tip | Dica
**FastAPI** suporta isso da mesma maneira que com strings simples:
-{* ../../docs_src/path_operation_configuration/tutorial002b.py hl[1,8:10,13,18] *}
+{* ../../docs_src/path_operation_configuration/tutorial002b_py39.py hl[1,8:10,13,18] *}
## Resumo e descrição { #summary-and-description }
Se você precisar marcar uma *operação de rota* como <abbr title="obsoleta, recomendada não usá-la">descontinuada</abbr>, mas sem removê-la, passe o parâmetro `deprecated`:
-{* ../../docs_src/path_operation_configuration/tutorial006.py hl[16] *}
+{* ../../docs_src/path_operation_configuration/tutorial006_py39.py hl[16] *}
Ela será claramente marcada como descontinuada nas documentações interativas:
Então, você pode declarar sua função assim:
-{* ../../docs_src/path_params_numeric_validations/tutorial002.py hl[7] *}
+{* ../../docs_src/path_params_numeric_validations/tutorial002_py39.py hl[7] *}
Mas tenha em mente que, se você usar `Annotated`, você não terá esse problema, não fará diferença, pois você não está usando valores padrão de parâmetros de função para `Query()` ou `Path()`.
O Python não fará nada com esse `*`, mas saberá que todos os parâmetros seguintes devem ser chamados como argumentos nomeados (pares chave-valor), também conhecidos como <abbr title="Do inglês: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>. Mesmo que eles não tenham um valor padrão.
-{* ../../docs_src/path_params_numeric_validations/tutorial003.py hl[7] *}
+{* ../../docs_src/path_params_numeric_validations/tutorial003_py39.py hl[7] *}
### Melhor com `Annotated` { #better-with-annotated }
Você pode declarar "parâmetros" ou "variáveis" de path com a mesma sintaxe usada por strings de formatação do Python:
-{* ../../docs_src/path_params/tutorial001.py hl[6:7] *}
+{* ../../docs_src/path_params/tutorial001_py39.py hl[6:7] *}
O valor do parâmetro de path `item_id` será passado para a sua função como o argumento `item_id`.
Você pode declarar o tipo de um parâmetro de path na função, usando as anotações de tipo padrão do Python:
-{* ../../docs_src/path_params/tutorial002.py hl[7] *}
+{* ../../docs_src/path_params/tutorial002_py39.py hl[7] *}
Neste caso, `item_id` é declarado como um `int`.
Como as *operações de rota* são avaliadas em ordem, você precisa garantir que o path para `/users/me` seja declarado antes do de `/users/{user_id}`:
-{* ../../docs_src/path_params/tutorial003.py hl[6,11] *}
+{* ../../docs_src/path_params/tutorial003_py39.py hl[6,11] *}
Caso contrário, o path para `/users/{user_id}` também corresponderia a `/users/me`, "achando" que está recebendo um parâmetro `user_id` com o valor `"me"`.
Da mesma forma, você não pode redefinir uma operação de rota:
-{* ../../docs_src/path_params/tutorial003b.py hl[6,11] *}
+{* ../../docs_src/path_params/tutorial003b_py39.py hl[6,11] *}
A primeira sempre será usada, já que o path corresponde primeiro.
Em seguida, crie atributos de classe com valores fixos, que serão os valores válidos disponíveis:
-{* ../../docs_src/path_params/tutorial005.py hl[1,6:9] *}
-
-/// info | Informação
-<a href="https://docs.python.org/3/library/enum.html" class="external-link" target="_blank">Enumerations (ou enums) estão disponíveis no Python</a> desde a versão 3.4.
-///
+{* ../../docs_src/path_params/tutorial005_py39.py hl[1,6:9] *}
/// tip | Dica
Se você está se perguntando, "AlexNet", "ResNet" e "LeNet" são apenas nomes de <abbr title="Tecnicamente, arquiteturas de modelos de Deep Learning">modelos</abbr> de Aprendizado de Máquina.
Em seguida, crie um *parâmetro de path* com anotação de tipo usando a classe enum que você criou (`ModelName`):
-{* ../../docs_src/path_params/tutorial005.py hl[16] *}
+{* ../../docs_src/path_params/tutorial005_py39.py hl[16] *}
### Verifique a documentação { #check-the-docs }
Você pode compará-lo com o *membro de enumeração* no seu enum `ModelName` criado:
-{* ../../docs_src/path_params/tutorial005.py hl[17] *}
+{* ../../docs_src/path_params/tutorial005_py39.py hl[17] *}
#### Obtenha o valor da enumeração { #get-the-enumeration-value }
Você pode obter o valor real (um `str` neste caso) usando `model_name.value`, ou, em geral, `your_enum_member.value`:
-{* ../../docs_src/path_params/tutorial005.py hl[20] *}
+{* ../../docs_src/path_params/tutorial005_py39.py hl[20] *}
/// tip | Dica
Você também pode acessar o valor `"lenet"` com `ModelName.lenet.value`.
Eles serão convertidos para seus valores correspondentes (strings neste caso) antes de serem retornados ao cliente:
-{* ../../docs_src/path_params/tutorial005.py hl[18,21,23] *}
+{* ../../docs_src/path_params/tutorial005_py39.py hl[18,21,23] *}
No seu cliente, você receberá uma resposta JSON como:
Então, você pode usá-lo com:
-{* ../../docs_src/path_params/tutorial004.py hl[6] *}
+{* ../../docs_src/path_params/tutorial004_py39.py hl[6] *}
/// tip | Dica
Você pode precisar que o parâmetro contenha `/home/johndoe/myfile.txt`, com uma barra inicial (`/`).
////
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
q: Union[str, None] = None
////
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
q: Annotated[Union[str, None]] = None
Quando você declara outros parâmetros na função que não fazem parte dos parâmetros da rota, esses parâmetros são automaticamente interpretados como parâmetros de "consulta".
-{* ../../docs_src/query_params/tutorial001.py hl[9] *}
+{* ../../docs_src/query_params/tutorial001_py39.py hl[9] *}
A consulta é o conjunto de pares chave-valor que vai depois de `?` na URL, separado pelo caractere `&`.
Porém, quando você quiser fazer com que o parâmetro de consulta seja obrigatório, você pode simplesmente não declarar nenhum valor como padrão.
-{* ../../docs_src/query_params/tutorial005.py hl[6:7] *}
+{* ../../docs_src/query_params/tutorial005_py39.py hl[6:7] *}
Aqui o parâmetro de consulta `needy` é um valor obrigatório, do tipo `str`.
O caso mais comum seria [retornar uma Response diretamente, conforme explicado posteriormente na documentação avançada](../advanced/response-directly.md){.internal-link target=_blank}.
-{* ../../docs_src/response_model/tutorial003_02.py hl[8,10:11] *}
+{* ../../docs_src/response_model/tutorial003_02_py39.py hl[8,10:11] *}
Este caso simples é tratado automaticamente pelo FastAPI porque a anotação do tipo de retorno é a classe (ou uma subclasse de) `Response`.
Você também pode usar uma subclasse de `Response` na anotação de tipo:
-{* ../../docs_src/response_model/tutorial003_03.py hl[8:9] *}
+{* ../../docs_src/response_model/tutorial003_03_py39.py hl[8:9] *}
Isso também funcionará porque `RedirectResponse` é uma subclasse de `Response`, e o FastAPI tratará automaticamente este caso simples.
* `@app.delete()`
* etc.
-{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
+{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
/// note | Nota
Vamos ver o exemplo anterior novamente:
-{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
+{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
`201` é o código de status para "Criado".
Você pode usar as variáveis de conveniência de `fastapi.status`.
-{* ../../docs_src/response_status_code/tutorial002.py hl[1,6] *}
+{* ../../docs_src/response_status_code/tutorial002_py39.py hl[1,6] *}
Eles são apenas uma conveniência, eles possuem o mesmo número, mas dessa forma você pode usar o preenchimento automático do editor para encontrá-los:
* Importe `StaticFiles`.
* "Monte" uma instância de `StaticFiles()` em um path específico.
-{* ../../docs_src/static_files/tutorial001.py hl[2,6] *}
+{* ../../docs_src/static_files/tutorial001_py39.py hl[2,6] *}
/// note | Detalhes Técnicos
Escreva instruções `assert` simples com as expressões Python padrão que você precisa verificar (novamente, `pytest` padrão).
-{* ../../docs_src/app_testing/tutorial001.py hl[2,12,15:18] *}
+{* ../../docs_src/app_testing/tutorial001_py39.py hl[2,12,15:18] *}
/// tip | Dica
No arquivo `main.py` você tem sua aplicação **FastAPI**:
-{* ../../docs_src/app_testing/main.py *}
+{* ../../docs_src/app_testing/app_a_py39/main.py *}
### Arquivo de teste { #testing-file }
Como esse arquivo está no mesmo pacote, você pode usar importações relativas para importar o objeto `app` do módulo `main` (`main.py`):
-{* ../../docs_src/app_testing/test_main.py hl[3] *}
+{* ../../docs_src/app_testing/app_a_py39/test_main.py hl[3] *}
...e ter o código para os testes como antes.
* list (as in Python list): list
* Machine Learning: Aprendizado de Máquina
* media type: media type (do not translate to "tipo de mídia")
-* non-Annotated: non-Annotated (do not translate non-Annotated when it comes after a Python version.e.g., “Python 3.8+ non-Annotated”)
+* non-Annotated: non-Annotated (do not translate non-Annotated when it comes after a Python version.e.g., “Python 3.10+ non-Annotated”)
* operation IDs: IDs de operação
* path (as in URL path): path
* path operation: operação de rota
Например, чтобы объявить ещё один ответ со статус-кодом `404` и Pydantic-моделью `Message`, можно написать:
-{* ../../docs_src/additional_responses/tutorial001.py hl[18,22] *}
+{* ../../docs_src/additional_responses/tutorial001_py39.py hl[18,22] *}
/// note | Примечание
А также ответ со статус-кодом `200`, который использует ваш `response_model`, но включает пользовательский `example`:
-{* ../../docs_src/additional_responses/tutorial003.py hl[20:31] *}
+{* ../../docs_src/additional_responses/tutorial003_py39.py hl[20:31] *}
Всё это будет объединено и включено в ваш OpenAPI и отображено в документации API:
Файл `main.py`:
-{* ../../docs_src/async_tests/main.py *}
+{* ../../docs_src/async_tests/app_a_py39/main.py *}
Файл `test_main.py` содержит тесты для `main.py`, теперь он может выглядеть так:
-{* ../../docs_src/async_tests/test_main.py *}
+{* ../../docs_src/async_tests/app_a_py39/test_main.py *}
## Запуск тестов { #run-it }
Маркер `@pytest.mark.anyio` говорит pytest, что тестовая функция должна быть вызвана асинхронно:
-{* ../../docs_src/async_tests/test_main.py hl[7] *}
+{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[7] *}
/// tip | Подсказка
Затем мы можем создать `AsyncClient` со ссылкой на приложение и посылать асинхронные запросы, используя `await`.
-{* ../../docs_src/async_tests/test_main.py hl[9:12] *}
+{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[9:12] *}
Это эквивалентно следующему:
Например, вы объявили операцию пути `/items/`:
-{* ../../docs_src/behind_a_proxy/tutorial001_01.py hl[6] *}
+{* ../../docs_src/behind_a_proxy/tutorial001_01_py39.py hl[6] *}
Если клиент обратится к `/items`, по умолчанию произойдёт редирект на `/items/`.
Хотя весь ваш код написан с расчётом, что путь один — `/app`.
-{* ../../docs_src/behind_a_proxy/tutorial001.py hl[6] *}
+{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[6] *}
Прокси будет «обрезать» префикс пути на лету перед передачей запроса на сервер приложения (скорее всего Uvicorn, запущенный через FastAPI CLI), поддерживая у вашего приложения иллюзию, что его обслуживают по `/app`, чтобы вам не пришлось менять весь код и добавлять префикс `/api/v1`.
Здесь мы добавляем его в сообщение лишь для демонстрации.
-{* ../../docs_src/behind_a_proxy/tutorial001.py hl[8] *}
+{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[8] *}
Затем, если вы запустите Uvicorn так:
Если нет возможности передать опцию командной строки `--root-path` (или аналог), вы можете указать параметр `root_path` при создании приложения FastAPI:
-{* ../../docs_src/behind_a_proxy/tutorial002.py hl[3] *}
+{* ../../docs_src/behind_a_proxy/tutorial002_py39.py hl[3] *}
Передача `root_path` в `FastAPI` эквивалентна опции командной строки `--root-path` для Uvicorn или Hypercorn.
Например:
-{* ../../docs_src/behind_a_proxy/tutorial003.py hl[4:7] *}
+{* ../../docs_src/behind_a_proxy/tutorial003_py39.py hl[4:7] *}
Будет сгенерирована схема OpenAPI примерно такая:
Если вы не хотите, чтобы FastAPI добавлял автоматический сервер, используя `root_path`, укажите параметр `root_path_in_servers=False`:
-{* ../../docs_src/behind_a_proxy/tutorial004.py hl[9] *}
+{* ../../docs_src/behind_a_proxy/tutorial004_py39.py hl[9] *}
и тогда этот сервер не будет добавлен в схему OpenAPI.
Но если вы уверены, что содержимое, которое вы возвращаете, **сериализуемо в JSON**, вы можете передать его напрямую в класс ответа и избежать дополнительных накладных расходов, которые FastAPI понёс бы, пропуская возвращаемое содержимое через `jsonable_encoder` перед передачей в класс ответа.
-{* ../../docs_src/custom_response/tutorial001b.py hl[2,7] *}
+{* ../../docs_src/custom_response/tutorial001b_py39.py hl[2,7] *}
/// info | Информация
- Импортируйте `HTMLResponse`.
- Передайте `HTMLResponse` в параметр `response_class` вашего декоратора операции пути.
-{* ../../docs_src/custom_response/tutorial002.py hl[2,7] *}
+{* ../../docs_src/custom_response/tutorial002_py39.py hl[2,7] *}
/// info | Информация
Тот же пример сверху, возвращающий `HTMLResponse`, может выглядеть так:
-{* ../../docs_src/custom_response/tutorial003.py hl[2,7,19] *}
+{* ../../docs_src/custom_response/tutorial003_py39.py hl[2,7,19] *}
/// warning | Предупреждение
Например, это может быть что-то вроде:
-{* ../../docs_src/custom_response/tutorial004.py hl[7,21,23] *}
+{* ../../docs_src/custom_response/tutorial004_py39.py hl[7,21,23] *}
В этом примере функция `generate_html_response()` уже генерирует и возвращает `Response` вместо возврата HTML в `str`.
FastAPI (фактически Starlette) автоматически добавит заголовок Content-Length. Также будет добавлен заголовок Content-Type, основанный на `media_type` и с добавлением charset для текстовых типов.
-{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
+{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
### `HTMLResponse` { #htmlresponse }
Принимает текст или байты и возвращает ответ в виде простого текста.
-{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *}
+{* ../../docs_src/custom_response/tutorial005_py39.py hl[2,7,9] *}
### `JSONResponse` { #jsonresponse }
///
-{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *}
+{* ../../docs_src/custom_response/tutorial001_py39.py hl[2,7] *}
/// tip | Совет
Вы можете вернуть `RedirectResponse` напрямую:
-{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *}
+{* ../../docs_src/custom_response/tutorial006_py39.py hl[2,9] *}
---
Или можно использовать его в параметре `response_class`:
-{* ../../docs_src/custom_response/tutorial006b.py hl[2,7,9] *}
+{* ../../docs_src/custom_response/tutorial006b_py39.py hl[2,7,9] *}
Если вы сделаете так, то сможете возвращать URL напрямую из своей функции-обработчика пути.
Также вы можете использовать параметр `status_code` в сочетании с параметром `response_class`:
-{* ../../docs_src/custom_response/tutorial006c.py hl[2,7,9] *}
+{* ../../docs_src/custom_response/tutorial006c_py39.py hl[2,7,9] *}
### `StreamingResponse` { #streamingresponse }
Принимает асинхронный генератор или обычный генератор/итератор и отправляет тело ответа потоково.
-{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *}
+{* ../../docs_src/custom_response/tutorial007_py39.py hl[2,14] *}
#### Использование `StreamingResponse` с файлоподобными объектами { #using-streamingresponse-with-file-like-objects }
Это включает многие библиотеки для работы с облачным хранилищем, обработки видео и т.д.
-{* ../../docs_src/custom_response/tutorial008.py hl[2,10:12,14] *}
+{* ../../docs_src/custom_response/tutorial008_py39.py hl[2,10:12,14] *}
1. Это функция-генератор. Она является «функцией-генератором», потому что содержит оператор(ы) `yield` внутри.
2. Используя блок `with`, мы гарантируем, что файлоподобный объект будет закрыт после завершения работы функции-генератора. То есть после того, как она закончит отправку ответа.
Файловые ответы будут содержать соответствующие заголовки `Content-Length`, `Last-Modified` и `ETag`.
-{* ../../docs_src/custom_response/tutorial009.py hl[2,10] *}
+{* ../../docs_src/custom_response/tutorial009_py39.py hl[2,10] *}
Вы также можете использовать параметр `response_class`:
-{* ../../docs_src/custom_response/tutorial009b.py hl[2,8,10] *}
+{* ../../docs_src/custom_response/tutorial009b_py39.py hl[2,8,10] *}
В этом случае вы можете возвращать путь к файлу напрямую из своей функции-обработчика пути.
Вы могли бы создать `CustomORJSONResponse`. Главное, что вам нужно сделать — реализовать метод `Response.render(content)`, который возвращает содержимое как `bytes`:
-{* ../../docs_src/custom_response/tutorial009c.py hl[9:14,17] *}
+{* ../../docs_src/custom_response/tutorial009c_py39.py hl[9:14,17] *}
Теперь вместо того, чтобы возвращать:
В примере ниже **FastAPI** будет использовать `ORJSONResponse` по умолчанию во всех операциях пути вместо `JSONResponse`.
-{* ../../docs_src/custom_response/tutorial010.py hl[2,4] *}
+{* ../../docs_src/custom_response/tutorial010_py39.py hl[2,4] *}
/// tip | Совет
Мы создаём асинхронную функцию `lifespan()` с `yield` примерно так:
-{* ../../docs_src/events/tutorial003.py hl[16,19] *}
+{* ../../docs_src/events/tutorial003_py39.py hl[16,19] *}
Здесь мы симулируем дорогую операцию startup по загрузке модели, помещая (фиктивную) функцию модели в словарь с моделями Машинного обучения до `yield`. Этот код будет выполнен до того, как приложение начнет принимать запросы, во время startup.
Первое, на что стоит обратить внимание, — мы определяем асинхронную функцию с `yield`. Это очень похоже на Зависимости с `yield`.
-{* ../../docs_src/events/tutorial003.py hl[14:19] *}
+{* ../../docs_src/events/tutorial003_py39.py hl[14:19] *}
Первая часть функции, до `yield`, будет выполнена до запуска приложения.
Это превращает функцию в «асинхронный менеджер контекста».
-{* ../../docs_src/events/tutorial003.py hl[1,13] *}
+{* ../../docs_src/events/tutorial003_py39.py hl[1,13] *}
Менеджер контекста в Python — это то, что можно использовать в операторе `with`. Например, `open()` можно использовать как менеджер контекста:
Параметр `lifespan` приложения `FastAPI` принимает асинхронный менеджер контекста, поэтому мы можем передать ему наш новый асинхронный менеджер контекста `lifespan`.
-{* ../../docs_src/events/tutorial003.py hl[22] *}
+{* ../../docs_src/events/tutorial003_py39.py hl[22] *}
## Альтернативные события (устаревшие) { #alternative-events-deprecated }
Чтобы добавить функцию, которую нужно запустить до старта приложения, объявите её как обработчик события `"startup"`:
-{* ../../docs_src/events/tutorial001.py hl[8] *}
+{* ../../docs_src/events/tutorial001_py39.py hl[8] *}
В этом случае функция-обработчик события `startup` инициализирует «базу данных» items (это просто `dict`) некоторыми значениями.
Чтобы добавить функцию, которую нужно запустить при завершении работы приложения, объявите её как обработчик события `"shutdown"`:
-{* ../../docs_src/events/tutorial002.py hl[6] *}
+{* ../../docs_src/events/tutorial002_py39.py hl[6] *}
Здесь функция-обработчик события `shutdown` запишет строку текста `"Application shutdown"` в файл `log.txt`.
Мы можем скачать OpenAPI JSON в файл `openapi.json`, а затем **убрать этот префикс‑тег** таким скриптом:
-{* ../../docs_src/generate_clients/tutorial004.py *}
+{* ../../docs_src/generate_clients/tutorial004_py39.py *}
//// tab | Node.js
Любой входящий запрос по `http` или `ws` будет перенаправлен на безопасную схему.
-{* ../../docs_src/advanced_middleware/tutorial001.py hl[2,6] *}
+{* ../../docs_src/advanced_middleware/tutorial001_py39.py hl[2,6] *}
## `TrustedHostMiddleware` { #trustedhostmiddleware }
Гарантирует, что во всех входящих запросах корректно установлен `Host`‑заголовок, чтобы защититься от атак на HTTP‑заголовок Host.
-{* ../../docs_src/advanced_middleware/tutorial002.py hl[2,6:8] *}
+{* ../../docs_src/advanced_middleware/tutorial002_py39.py hl[2,6:8] *}
Поддерживаются следующие аргументы:
Это middleware обрабатывает как обычные, так и потоковые ответы.
-{* ../../docs_src/advanced_middleware/tutorial003.py hl[2,6] *}
+{* ../../docs_src/advanced_middleware/tutorial003_py39.py hl[2,6] *}
Поддерживаются следующие аргументы:
При создании приложения на **FastAPI** есть атрибут `webhooks`, с помощью которого можно объявлять вебхуки так же, как вы объявляете операции пути (обработчики пути), например с `@app.webhooks.post()`.
-{* ../../docs_src/openapi_webhooks/tutorial001.py hl[9:13,36:53] *}
+{* ../../docs_src/openapi_webhooks/tutorial001_py39.py hl[9:13,36:53] *}
Определенные вами вебхуки попадут в схему **OpenAPI** и в автоматический **интерфейс документации**.
Нужно убедиться, что он уникален для каждой операции.
-{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *}
### Использование имени функции-обработчика пути как operationId { #using-the-path-operation-function-name-as-the-operationid }
Делать это следует после добавления всех *операций пути*.
-{* ../../docs_src/path_operation_advanced_configuration/tutorial002.py hl[2, 12:21, 24] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py39.py hl[2, 12:21, 24] *}
/// tip | Совет
Чтобы исключить *операцию пути* из генерируемой схемы OpenAPI (а значит, и из автоматической документации), используйте параметр `include_in_schema` и установите его в `False`:
-{* ../../docs_src/path_operation_advanced_configuration/tutorial003.py hl[6] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py39.py hl[6] *}
## Расширенное описание из docstring { #advanced-description-from-docstring }
`openapi_extra` может пригодиться, например, чтобы объявить [Расширения OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
-{* ../../docs_src/path_operation_advanced_configuration/tutorial005.py hl[6] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py39.py hl[6] *}
Если вы откроете автоматическую документацию API, ваше расширение появится внизу страницы конкретной *операции пути*.
Это можно сделать с помощью `openapi_extra`:
-{* ../../docs_src/path_operation_advanced_configuration/tutorial006.py hl[19:36, 39:40] *}
+{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *}
В этом примере мы не объявляли никакую Pydantic-модель. Фактически тело запроса даже не <abbr title="преобразовано из простого формата, например байтов, в объекты Python">распарсено</abbr> как JSON, оно читается напрямую как `bytes`, а функция `magic_data_reader()` будет отвечать за его парсинг каким-то способом.
И затем вы можете установить `status_code` в этом *временном* объекте ответа.
-{* ../../docs_src/response_change_status_code/tutorial001.py hl[1,9,12] *}
+{* ../../docs_src/response_change_status_code/tutorial001_py39.py hl[1,9,12] *}
После этого вы можете вернуть любой объект, который вам нужен, как обычно (`dict`, модель базы данных и т.д.).
Затем установить cookies в этом временном объекте ответа.
-{* ../../docs_src/response_cookies/tutorial002.py hl[1, 8:9] *}
+{* ../../docs_src/response_cookies/tutorial002_py39.py hl[1, 8:9] *}
После этого можно вернуть любой объект, как и раньше (например, `dict`, объект модели базы данных и так далее).
Затем установите cookies и верните этот объект:
-{* ../../docs_src/response_cookies/tutorial001.py hl[10:12] *}
+{* ../../docs_src/response_cookies/tutorial001_py39.py hl[10:12] *}
/// tip | Совет
Вы можете поместить ваш XML-контент в строку, поместить её в `Response` и вернуть:
-{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
+{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
## Примечания { #notes }
А затем вы можете устанавливать HTTP-заголовки в этом *временном* объекте ответа.
-{* ../../docs_src/response_headers/tutorial002.py hl[1, 7:8] *}
+{* ../../docs_src/response_headers/tutorial002_py39.py hl[1, 7:8] *}
После этого вы можете вернуть любой нужный объект, как обычно (например, `dict`, модель из базы данных и т.д.).
Создайте ответ, как описано в [Вернуть Response напрямую](response-directly.md){.internal-link target=_blank}, и передайте заголовки как дополнительный параметр:
-{* ../../docs_src/response_headers/tutorial001.py hl[10:12] *}
+{* ../../docs_src/response_headers/tutorial001_py39.py hl[10:12] *}
/// note | Технические детали
//// tab | Pydantic v2
-{* ../../docs_src/settings/tutorial001.py hl[2,5:8,11] *}
+{* ../../docs_src/settings/tutorial001_py39.py hl[2,5:8,11] *}
////
///
-{* ../../docs_src/settings/tutorial001_pv1.py hl[2,5:8,11] *}
+{* ../../docs_src/settings/tutorial001_pv1_py39.py hl[2,5:8,11] *}
////
Затем вы можете использовать новый объект `settings` в вашем приложении:
-{* ../../docs_src/settings/tutorial001.py hl[18:20] *}
+{* ../../docs_src/settings/tutorial001_py39.py hl[18:20] *}
### Запуск сервера { #run-the-server }
Например, у вас может быть файл `config.py` со следующим содержимым:
-{* ../../docs_src/settings/app01/config.py *}
+{* ../../docs_src/settings/app01_py39/config.py *}
А затем использовать его в файле `main.py`:
-{* ../../docs_src/settings/app01/main.py hl[3,11:13] *}
+{* ../../docs_src/settings/app01_py39/main.py hl[3,11:13] *}
/// tip | Совет
Сначала создайте основное, верхнего уровня, приложение **FastAPI** и его *операции пути*:
-{* ../../docs_src/sub_applications/tutorial001.py hl[3, 6:8] *}
+{* ../../docs_src/sub_applications/tutorial001_py39.py hl[3, 6:8] *}
### Подприложение { #sub-application }
Это подприложение — обычное стандартное приложение FastAPI, но именно оно будет «смонтировано»:
-{* ../../docs_src/sub_applications/tutorial001.py hl[11, 14:16] *}
+{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 14:16] *}
### Смонтируйте подприложение { #mount-the-sub-application }
В этом случае оно будет смонтировано по пути `/subapi`:
-{* ../../docs_src/sub_applications/tutorial001.py hl[11, 19] *}
+{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 19] *}
### Проверьте автоматическую документацию API { #check-the-automatic-api-docs }
- Объявите параметр `Request` в *операции пути*, которая будет возвращать шаблон.
- Используйте созданный `templates`, чтобы отрендерить и вернуть `TemplateResponse`; передайте имя шаблона, объект `request` и словарь «context» с парами ключ-значение для использования внутри шаблона Jinja2.
-{* ../../docs_src/templates/tutorial001.py hl[4,11,15:18] *}
+{* ../../docs_src/templates/tutorial001_py39.py hl[4,11,15:18] *}
/// note | Примечание
Если вам нужно, чтобы `lifespan` выполнялся в ваших тестах, вы можете использовать `TestClient` вместе с оператором `with`:
-{* ../../docs_src/app_testing/tutorial004.py hl[9:15,18,27:28,30:32,41:43] *}
+{* ../../docs_src/app_testing/tutorial004_py39.py hl[9:15,18,27:28,30:32,41:43] *}
Вы можете узнать больше подробностей в статье [Запуск lifespan в тестах на официальном сайте документации Starlette.](https://www.starlette.dev/lifespan/#running-lifespan-in-tests)
Для устаревших событий `startup` и `shutdown` вы можете использовать `TestClient` следующим образом:
-{* ../../docs_src/app_testing/tutorial003.py hl[9:12,20:24] *}
+{* ../../docs_src/app_testing/tutorial003_py39.py hl[9:12,20:24] *}
Для этого используйте `TestClient` с менеджером контекста `with`, подключаясь к WebSocket:
-{* ../../docs_src/app_testing/tutorial002.py hl[27:31] *}
+{* ../../docs_src/app_testing/tutorial002_py39.py hl[27:31] *}
/// note | Примечание
Для этого нужно обратиться к запросу напрямую.
-{* ../../docs_src/using_request_directly/tutorial001.py hl[1,7:8] *}
+{* ../../docs_src/using_request_directly/tutorial001_py39.py hl[1,7:8] *}
Если объявить параметр *функции-обработчика пути* с типом `Request`, **FastAPI** поймёт, что нужно передать объект `Request` в этот параметр.
Для примера нам нужен наиболее простой способ, который позволит сосредоточиться на серверной части веб‑сокетов и получить рабочий код:
-{* ../../docs_src/websockets/tutorial001.py hl[2,6:38,41:43] *}
+{* ../../docs_src/websockets/tutorial001_py39.py hl[2,6:38,41:43] *}
## Создание `websocket` { #create-a-websocket }
Создайте `websocket` в своем **FastAPI** приложении:
-{* ../../docs_src/websockets/tutorial001.py hl[1,46:47] *}
+{* ../../docs_src/websockets/tutorial001_py39.py hl[1,46:47] *}
/// note | Технические детали
Через эндпоинт веб-сокета вы можете получать и отправлять сообщения.
-{* ../../docs_src/websockets/tutorial001.py hl[48:52] *}
+{* ../../docs_src/websockets/tutorial001_py39.py hl[48:52] *}
Вы можете получать и отправлять двоичные, текстовые и JSON данные.
После этого смонтируйте его на путь.
-{* ../../docs_src/wsgi/tutorial001.py hl[2:3,3] *}
+{* ../../docs_src/wsgi/tutorial001_py39.py hl[2:3,3] *}
## Проверьте { #check-it }
Например:
-{* ../../docs_src/conditional_openapi/tutorial001.py hl[6,11] *}
+{* ../../docs_src/conditional_openapi/tutorial001_py39.py hl[6,11] *}
Здесь мы объявляем настройку `openapi_url` с тем же значением по умолчанию — `"/openapi.json"`.
Но вы можете отключить её, установив `syntaxHighlight` в `False`:
-{* ../../docs_src/configure_swagger_ui/tutorial001.py hl[3] *}
+{* ../../docs_src/configure_swagger_ui/tutorial001_py39.py hl[3] *}
…и после этого Swagger UI больше не будет показывать подсветку синтаксиса:
Аналогично вы можете задать тему подсветки синтаксиса с ключом "syntaxHighlight.theme" (обратите внимание, что посередине стоит точка):
-{* ../../docs_src/configure_swagger_ui/tutorial002.py hl[3] *}
+{* ../../docs_src/configure_swagger_ui/tutorial002_py39.py hl[3] *}
Эта настройка изменит цветовую тему подсветки синтаксиса:
Например, чтобы отключить `deepLinking`, можно передать такие настройки в `swagger_ui_parameters`:
-{* ../../docs_src/configure_swagger_ui/tutorial003.py hl[3] *}
+{* ../../docs_src/configure_swagger_ui/tutorial003_py39.py hl[3] *}
## Другие параметры Swagger UI { #other-swagger-ui-parameters }
Чтобы отключить её, установите их URL в значение `None` при создании вашего приложения `FastAPI`:
-{* ../../docs_src/custom_docs_ui/tutorial001.py hl[8] *}
+{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[8] *}
### Подключить пользовательскую документацию { #include-the-custom-docs }
Аналогично и для ReDoc...
-{* ../../docs_src/custom_docs_ui/tutorial001.py hl[2:6,11:19,22:24,27:33] *}
+{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[2:6,11:19,22:24,27:33] *}
/// tip | Совет
Чтобы убедиться, что всё работает, создайте *операцию пути*:
-{* ../../docs_src/custom_docs_ui/tutorial001.py hl[36:38] *}
+{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[36:38] *}
### Тестирование { #test-it }
* Импортируйте `StaticFiles`.
* Смонтируйте экземпляр `StaticFiles()` в определённый путь.
-{* ../../docs_src/custom_docs_ui/tutorial002.py hl[7,11] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[7,11] *}
### Протестируйте статические файлы { #test-the-static-files }
Чтобы отключить её, установите их URL в значение `None` при создании вашего приложения `FastAPI`:
-{* ../../docs_src/custom_docs_ui/tutorial002.py hl[9] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[9] *}
### Подключить пользовательскую документацию со статическими файлами { #include-the-custom-docs-for-static-files }
Аналогично и для ReDoc...
-{* ../../docs_src/custom_docs_ui/tutorial002.py hl[2:6,14:22,25:27,30:36] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[2:6,14:22,25:27,30:36] *}
/// tip | Совет
Чтобы убедиться, что всё работает, создайте *операцию пути*:
-{* ../../docs_src/custom_docs_ui/tutorial002.py hl[39:41] *}
+{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[39:41] *}
### Тестирование UI со статическими файлами { #test-static-files-ui }
Сначала напишите приложение **FastAPI** как обычно:
-{* ../../docs_src/extending_openapi/tutorial001.py hl[1,4,7:9] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[1,4,7:9] *}
### Сгенерируйте схему OpenAPI { #generate-the-openapi-schema }
Затем используйте ту же вспомогательную функцию для генерации схемы OpenAPI внутри функции `custom_openapi()`:
-{* ../../docs_src/extending_openapi/tutorial001.py hl[2,15:21] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[2,15:21] *}
### Измените схему OpenAPI { #modify-the-openapi-schema }
Теперь можно добавить расширение ReDoc, добавив кастомный `x-logo` в «объект» `info` в схеме OpenAPI:
-{* ../../docs_src/extending_openapi/tutorial001.py hl[22:24] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[22:24] *}
### Кэшируйте схему OpenAPI { #cache-the-openapi-schema }
Она будет создана один раз, а затем тот же кэшированный вариант будет использоваться для последующих запросов.
-{* ../../docs_src/extending_openapi/tutorial001.py hl[13:14,25:26] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[13:14,25:26] *}
### Переопределите метод { #override-the-method }
Теперь вы можете заменить метод `.openapi()` на вашу новую функцию.
-{* ../../docs_src/extending_openapi/tutorial001.py hl[29] *}
+{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[29] *}
### Проверьте { #check-it }
Вот небольшой пример того, как можно интегрировать Strawberry с FastAPI:
-{* ../../docs_src/graphql/tutorial001.py hl[3,22,25] *}
+{* ../../docs_src/graphql/tutorial001_py39.py hl[3,22,25] *}
Подробнее о Strawberry можно узнать в <a href="https://strawberry.rocks/" class="external-link" target="_blank">документации Strawberry</a>.
Давайте начнем с простого примера:
-{* ../../docs_src/python_types/tutorial001.py *}
+{* ../../docs_src/python_types/tutorial001_py39.py *}
Вызов этой программы выводит:
* Преобразует первую букву каждого значения в верхний регистр с помощью `title()`.
* <abbr title="Объединяет в одно целое. Содержимое одного — сразу после другого.">Соединяет</abbr> их пробелом посередине.
-{* ../../docs_src/python_types/tutorial001.py hl[2] *}
+{* ../../docs_src/python_types/tutorial001_py39.py hl[2] *}
### Отредактируем пример { #edit-it }
Это и есть «подсказки типов»:
-{* ../../docs_src/python_types/tutorial002.py hl[1] *}
+{* ../../docs_src/python_types/tutorial002_py39.py hl[1] *}
Это не то же самое, что объявление значений по умолчанию, как, например:
Посмотрите на эту функцию — у неё уже есть подсказки типов:
-{* ../../docs_src/python_types/tutorial003.py hl[1] *}
+{* ../../docs_src/python_types/tutorial003_py39.py hl[1] *}
Так как редактор кода знает типы переменных, вы получаете не только автозавершение, но и проверки ошибок:
Теперь вы знаете, что нужно исправить — преобразовать `age` в строку с помощью `str(age)`:
-{* ../../docs_src/python_types/tutorial004.py hl[2] *}
+{* ../../docs_src/python_types/tutorial004_py39.py hl[2] *}
## Объявление типов { #declaring-types }
* `bool`
* `bytes`
-{* ../../docs_src/python_types/tutorial005.py hl[1] *}
+{* ../../docs_src/python_types/tutorial005_py39.py hl[1] *}
### Generic-типы с параметрами типов { #generic-types-with-type-parameters }
Например, давайте определим переменную как `list` из `str`.
-//// tab | Python 3.9+
-
Объявите переменную с тем же синтаксисом двоеточия (`:`).
В качестве типа укажите `list`.
Так как список — это тип, содержащий внутренние типы, укажите их в квадратных скобках:
-```Python hl_lines="1"
-{!> ../../docs_src/python_types/tutorial006_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-Из `typing` импортируйте `List` (с заглавной `L`):
-
-```Python hl_lines="1"
-{!> ../../docs_src/python_types/tutorial006.py!}
-```
-
-Объявите переменную с тем же синтаксисом двоеточия (`:`).
-
-В качестве типа используйте `List`, который вы импортировали из `typing`.
-
-Так как список — это тип, содержащий внутренние типы, укажите их в квадратных скобках:
-
-```Python hl_lines="4"
-{!> ../../docs_src/python_types/tutorial006.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial006_py39.py hl[1] *}
/// info | Информация
Эти внутренние типы в квадратных скобках называются «параметрами типов».
-В данном случае `str` — это параметр типа, передаваемый в `List` (или `list` в Python 3.9 и выше).
+В данном случае `str` — это параметр типа, передаваемый в `list`.
///
Это означает: «переменная `items` — это `list`, и каждый элемент этого списка — `str`».
-/// tip | Совет
-
-Если вы используете Python 3.9 или выше, вам не нужно импортировать `List` из `typing`, можно использовать обычный встроенный тип `list`.
-
-///
-
Таким образом, ваш редактор кода сможет помогать даже при обработке элементов списка:
<img src="/img/python-types/image05.png">
Аналогично вы бы объявили `tuple` и `set`:
-//// tab | Python 3.9+
-
-```Python hl_lines="1"
-{!> ../../docs_src/python_types/tutorial007_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial007.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial007_py39.py hl[1] *}
Это означает:
Второй параметр типа — для значений `dict`:
-//// tab | Python 3.9+
-
-```Python hl_lines="1"
-{!> ../../docs_src/python_types/tutorial008_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial008.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial008_py39.py hl[1] *}
Это означает:
////
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial008b.py!}
+{!> ../../docs_src/python_types/tutorial008b_py39.py!}
```
////
В Python 3.6 и выше (включая Python 3.10) это можно объявить, импортировав и используя `Optional` из модуля `typing`.
```Python hl_lines="1 4"
-{!../../docs_src/python_types/tutorial009.py!}
+{!../../docs_src/python_types/tutorial009_py39.py!}
```
Использование `Optional[str]` вместо просто `str` позволит редактору кода помочь вам обнаружить ошибки, когда вы предполагаете, что значение всегда `str`, хотя на самом деле оно может быть и `None`.
////
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial009.py!}
+{!> ../../docs_src/python_types/tutorial009_py39.py!}
```
////
-//// tab | Python 3.8+ альтернативный вариант
+//// tab | Python 3.9+ альтернативный вариант
```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial009b.py!}
+{!> ../../docs_src/python_types/tutorial009b_py39.py!}
```
////
В качестве примера возьмём эту функцию:
-{* ../../docs_src/python_types/tutorial009c.py hl[1,4] *}
+{* ../../docs_src/python_types/tutorial009c_py39.py hl[1,4] *}
Параметр `name` определён как `Optional[str]`, но он **не необязательный** — вы не можете вызвать функцию без этого параметра:
* `set`
* `dict`
-И, как и в Python 3.8, из модуля `typing`:
+И, как и в предыдущих версиях Python, из модуля `typing`:
* `Union`
-* `Optional` (так же, как в Python 3.8)
+* `Optional`
* ...и другие.
В Python 3.10, как альтернативу generics `Union` и `Optional`, можно использовать <abbr title='также называется «побитовый оператор OR», но это значение здесь нерелевантно'>вертикальную черту (`|`)</abbr> для объявления объединений типов — это гораздо лучше и проще.
* `set`
* `dict`
-И, как и в Python 3.8, из модуля `typing`:
+И generics из модуля `typing`:
* `Union`
* `Optional`
////
-//// tab | Python 3.8+
-
-* `List`
-* `Tuple`
-* `Set`
-* `Dict`
-* `Union`
-* `Optional`
-* ...и другие.
-
-////
-
### Классы как типы { #classes-as-types }
Вы также можете объявлять класс как тип переменной.
Допустим, у вас есть класс `Person` с именем:
-{* ../../docs_src/python_types/tutorial010.py hl[1:3] *}
+{* ../../docs_src/python_types/tutorial010_py39.py hl[1:3] *}
Тогда вы можете объявить переменную типа `Person`:
-{* ../../docs_src/python_types/tutorial010.py hl[6] *}
+{* ../../docs_src/python_types/tutorial010_py39.py hl[6] *}
И снова вы получите полную поддержку редактора кода:
Пример из официальной документации Pydantic:
-//// tab | Python 3.10+
-
-```Python
-{!> ../../docs_src/python_types/tutorial011_py310.py!}
-```
-
-////
-
-//// tab | Python 3.9+
-
-```Python
-{!> ../../docs_src/python_types/tutorial011_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-```Python
-{!> ../../docs_src/python_types/tutorial011.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial011_py310.py *}
/// info | Информация
В Python также есть возможность добавлять **дополнительные <abbr title="Данные о данных, в данном случае — информация о типе, например описание.">метаданные</abbr>** к подсказкам типов с помощью `Annotated`.
-//// tab | Python 3.9+
-
-В Python 3.9 `Annotated` входит в стандартную библиотеку, поэтому вы можете импортировать его из `typing`.
+Начиная с Python 3.9, `Annotated` входит в стандартную библиотеку, поэтому вы можете импортировать его из `typing`.
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial013_py39.py!}
-```
-
-////
-
-//// tab | Python 3.8+
-
-В версиях ниже Python 3.9 импортируйте `Annotated` из `typing_extensions`.
-
-Он уже будет установлен вместе с **FastAPI**.
-
-```Python hl_lines="1 4"
-{!> ../../docs_src/python_types/tutorial013.py!}
-```
-
-////
+{* ../../docs_src/python_types/tutorial013_py39.py hl[1,4] *}
Сам Python ничего не делает с `Annotated`. А для редакторов кода и других инструментов тип по-прежнему `str`.
...и **FastAPI** использует эти же объявления для:
-* **Определения требований**: из path-параметров, query-параметров, HTTP-заголовков, тел запросов, зависимостей и т.д.
+* **Определения требований**: из path-параметров пути запроса, query-параметров, HTTP-заголовков, тел запросов, зависимостей и т.д.
* **Преобразования данных**: из HTTP-запроса к требуемому типу.
* **Валидации данных**: приходящих с каждого HTTP-запроса:
* Генерации **автоматических ошибок**, возвращаемых клиенту, когда данные некорректны.
Сначала импортируйте `BackgroundTasks` и объявите параметр в вашей функции‑обработчике пути с типом `BackgroundTasks`:
-{* ../../docs_src/background_tasks/tutorial001.py hl[1,13] *}
+{* ../../docs_src/background_tasks/tutorial001_py39.py hl[1,13] *}
**FastAPI** создаст объект типа `BackgroundTasks` для вас и передаст его через этот параметр.
Так как операция записи не использует `async` и `await`, мы определим функцию как обычную `def`:
-{* ../../docs_src/background_tasks/tutorial001.py hl[6:9] *}
+{* ../../docs_src/background_tasks/tutorial001_py39.py hl[6:9] *}
## Добавление фоновой задачи { #add-the-background-task }
Внутри вашей функции‑обработчика пути передайте функцию задачи объекту фоновых задач методом `.add_task()`:
-{* ../../docs_src/background_tasks/tutorial001.py hl[14] *}
+{* ../../docs_src/background_tasks/tutorial001_py39.py hl[14] *}
`.add_task()` принимает следующие аргументы:
В Python есть специальный способ объявлять списки с внутренними типами, или «параметрами типа»:
-### Импортируйте `List` из модуля typing { #import-typings-list }
-
-В Python 3.9 и выше вы можете использовать стандартный тип `list` для объявления аннотаций типов, как мы увидим ниже. 💡
-
-Но в версиях Python до 3.9 (начиная с 3.6) сначала вам необходимо импортировать `List` из стандартного модуля `typing`:
-
-{* ../../docs_src/body_nested_models/tutorial002.py hl[1] *}
-
### Объявите `list` с параметром типа { #declare-a-list-with-a-type-parameter }
-Для объявления типов, у которых есть параметры типа (внутренние типы), таких как `list`, `dict`, `tuple`:
-
-* Если у вас Python версии ниже 3.9, импортируйте их аналоги из модуля `typing`
-* Передайте внутренний(ие) тип(ы) как «параметры типа», используя квадратные скобки: `[` и `]`
-
-В Python 3.9 это будет:
+Для объявления типов, у которых есть параметры типа (внутренние типы), таких как `list`, `dict`, `tuple`, передайте внутренний(ие) тип(ы) как «параметры типа», используя квадратные скобки: `[` и `]`
```Python
my_list: list[str]
```
-В версиях Python до 3.9 это будет:
-
-```Python
-from typing import List
-
-my_list: List[str]
-```
-
Это всё стандартный синтаксис Python для объявления типов.
Используйте этот же стандартный синтаксис для атрибутов модели с внутренними типами.
Ещё раз: сделав такое объявление, с помощью **FastAPI** вы получите:
-* Поддержку редактора кода (автозавершение и т. д.), даже для вложенных моделей
+* Поддержку редактора кода (автозавершение и т.д.), даже для вложенных моделей
* Преобразование данных
* Валидацию данных
* Автоматическую документацию
Если верхний уровень значения тела JSON-объекта представляет собой JSON `array` (в Python — `list`), вы можете объявить тип в параметре функции, так же как в моделях Pydantic:
-```Python
-images: List[Image]
-```
-
-или в Python 3.9 и выше:
-
```Python
images: list[Image]
```
FastAPI понимает, что значение `q` не является обязательным из-за значения по умолчанию `= None`.
-Аннотации типов `str | None` (Python 3.10+) или `Union[str, None]` (Python 3.8+) не используются FastAPI для определения обязательности; он узнает, что параметр не обязателен, потому что у него есть значение по умолчанию `= None`.
+Аннотации типов `str | None` (Python 3.10+) или `Union[str, None]` (Python 3.9+) не используются FastAPI для определения обязательности; он узнает, что параметр не обязателен, потому что у него есть значение по умолчанию `= None`.
Но добавление аннотаций типов позволит вашему редактору кода лучше вас поддерживать и обнаруживать ошибки.
* Отдельных HTTP-методов (`POST`, `PUT`) или всех вместе, используя `"*"`.
* Отдельных HTTP-заголовков или всех вместе, используя `"*"`.
-{* ../../docs_src/cors/tutorial001.py hl[2,6:11,13:19] *}
+{* ../../docs_src/cors/tutorial001_py39.py hl[2,6:11,13:19] *}
`CORSMiddleware` использует "запрещающие" значения по умолчанию, поэтому вам нужно явным образом разрешить использование отдельных источников, методов или заголовков, чтобы браузеры могли использовать их в кросс-доменном контексте.
В вашем FastAPI приложении, импортируйте и вызовите `uvicorn` напрямую:
-{* ../../docs_src/debugging/tutorial001.py hl[1,15] *}
+{* ../../docs_src/debugging/tutorial001_py39.py hl[1,15] *}
### Описание `__name__ == "__main__"` { #about-name-main }
Обратите внимание, что в приведенном выше коде мы два раза пишем `CommonQueryParams`:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
-//// tab | Python 3.8+ non-Annotated
+//// tab | Python 3.9+ non-Annotated
/// tip | Подсказка
В этом случае первый `CommonQueryParams`, в:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, ...
////
-//// tab | Python 3.8+ non-Annotated
+//// tab | Python 3.9+ non-Annotated
/// tip | Подсказка
На самом деле можно написать просто:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[Any, Depends(CommonQueryParams)]
////
-//// tab | Python 3.8+ non-Annotated
+//// tab | Python 3.9+ non-Annotated
/// tip | Подсказка
Но вы видите, что здесь мы имеем некоторое повторение кода, дважды написав `CommonQueryParams`:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
-//// tab | Python 3.8+ non-Annotated
+//// tab | Python 3.9+ non-Annotated
/// tip | Подсказка
Вместо того чтобы писать:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
-//// tab | Python 3.8+ non-Annotated
+//// tab | Python 3.9+ non-Annotated
/// tip | Подсказка
...следует написать:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
commons: Annotated[CommonQueryParams, Depends()]
////
-//// tab | Python 3.8 non-Annotated
+//// tab | Python 3.9+ non-Annotated
/// tip | Подсказка
Перед созданием ответа будет выполнен только код до и включая оператор `yield`:
-{* ../../docs_src/dependencies/tutorial007.py hl[2:4] *}
+{* ../../docs_src/dependencies/tutorial007_py39.py hl[2:4] *}
Значение, полученное из `yield`, внедряется в *операции пути* и другие зависимости:
-{* ../../docs_src/dependencies/tutorial007.py hl[4] *}
+{* ../../docs_src/dependencies/tutorial007_py39.py hl[4] *}
Код, следующий за оператором `yield`, выполняется после ответа:
-{* ../../docs_src/dependencies/tutorial007.py hl[5:6] *}
+{* ../../docs_src/dependencies/tutorial007_py39.py hl[5:6] *}
/// tip | Подсказка
Точно так же можно использовать `finally`, чтобы убедиться, что обязательные шаги при выходе выполнены независимо от того, было ли исключение или нет.
-{* ../../docs_src/dependencies/tutorial007.py hl[3,5] *}
+{* ../../docs_src/dependencies/tutorial007_py39.py hl[3,5] *}
## Подзависимости с `yield` { #sub-dependencies-with-yield }
Их также можно использовать внутри зависимостей **FastAPI** с `yield`, применяя операторы
`with` или `async with` внутри функции зависимости:
-{* ../../docs_src/dependencies/tutorial010.py hl[1:9,13] *}
+{* ../../docs_src/dependencies/tutorial010_py39.py hl[1:9,13] *}
/// tip | Подсказка
В этом случае они будут применяться ко всем *операциям пути* в приложении:
-{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[16] *}
+{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[17] *}
Все способы [добавления `dependencies` (зависимостей) в *декораторах операций пути*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} по-прежнему применимы, но в данном случае зависимости применяются ко всем *операциям пути* приложения.
В расширенном сценарии, когда вы знаете, что вам нужно, чтобы зависимость вызывалась на каждом шаге (возможно, несколько раз) в одном и том же запросе, вместо использования "кэшированного" значения, вы можете установить параметр `use_cache=False` при использовании `Depends`:
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python hl_lines="1"
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
////
-//// tab | Python 3.8+ без Annotated
+//// tab | Python 3.9+ без Annotated
/// tip | Подсказка
Самый простой файл FastAPI может выглядеть так:
-{* ../../docs_src/first_steps/tutorial001.py *}
+{* ../../docs_src/first_steps/tutorial001_py39.py *}
Скопируйте это в файл `main.py`.
### Шаг 1: импортируйте `FastAPI` { #step-1-import-fastapi }
-{* ../../docs_src/first_steps/tutorial001.py hl[1] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[1] *}
`FastAPI` — это класс на Python, который предоставляет всю функциональность для вашего API.
### Шаг 2: создайте экземпляр `FastAPI` { #step-2-create-a-fastapi-instance }
-{* ../../docs_src/first_steps/tutorial001.py hl[3] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[3] *}
Здесь переменная `app` будет экземпляром класса `FastAPI`.
#### Определите *декоратор операции пути (path operation decorator)* { #define-a-path-operation-decorator }
-{* ../../docs_src/first_steps/tutorial001.py hl[6] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[6] *}
`@app.get("/")` сообщает **FastAPI**, что функция прямо под ним отвечает за обработку запросов, поступающих:
* **операция**: `get`.
* **функция**: функция ниже «декоратора» (ниже `@app.get("/")`).
-{* ../../docs_src/first_steps/tutorial001.py hl[7] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[7] *}
Это функция на Python.
Вы также можете определить её как обычную функцию вместо `async def`:
-{* ../../docs_src/first_steps/tutorial003.py hl[7] *}
+{* ../../docs_src/first_steps/tutorial003_py39.py hl[7] *}
/// note | Примечание
### Шаг 5: верните содержимое { #step-5-return-the-content }
-{* ../../docs_src/first_steps/tutorial001.py hl[8] *}
+{* ../../docs_src/first_steps/tutorial001_py39.py hl[8] *}
Вы можете вернуть `dict`, `list`, отдельные значения `str`, `int` и т.д.
### Импортируйте `HTTPException` { #import-httpexception }
-{* ../../docs_src/handling_errors/tutorial001.py hl[1] *}
+{* ../../docs_src/handling_errors/tutorial001_py39.py hl[1] *}
### Вызовите `HTTPException` в своем коде { #raise-an-httpexception-in-your-code }
В данном примере, когда клиент запрашивает элемент по несуществующему ID, возникает исключение со статус-кодом `404`:
-{* ../../docs_src/handling_errors/tutorial001.py hl[11] *}
+{* ../../docs_src/handling_errors/tutorial001_py39.py hl[11] *}
### Возвращаемый ответ { #the-resulting-response }
Но в случае, если это необходимо для продвинутого сценария, можно добавить пользовательские заголовки:
-{* ../../docs_src/handling_errors/tutorial002.py hl[14] *}
+{* ../../docs_src/handling_errors/tutorial002_py39.py hl[14] *}
## Установка пользовательских обработчиков исключений { #install-custom-exception-handlers }
Можно добавить собственный обработчик исключений с помощью `@app.exception_handler()`:
-{* ../../docs_src/handling_errors/tutorial003.py hl[5:7,13:18,24] *}
+{* ../../docs_src/handling_errors/tutorial003_py39.py hl[5:7,13:18,24] *}
Здесь, если запросить `/unicorns/yolo`, то *операция пути* вызовет `UnicornException`.
Обработчик исключения получит объект `Request` и исключение.
-{* ../../docs_src/handling_errors/tutorial004.py hl[2,14:19] *}
+{* ../../docs_src/handling_errors/tutorial004_py39.py hl[2,14:19] *}
Теперь, если перейти к `/items/foo`, то вместо стандартной JSON-ошибки с:
Например, для этих ошибок можно вернуть обычный текстовый ответ вместо JSON:
-{* ../../docs_src/handling_errors/tutorial004.py hl[3:4,9:11,25] *}
+{* ../../docs_src/handling_errors/tutorial004_py39.py hl[3:4,9:11,25] *}
/// note | Технические детали
Вы можете использовать его при разработке приложения для регистрации тела и его отладки, возврата пользователю и т.д.
-{* ../../docs_src/handling_errors/tutorial005.py hl[14] *}
+{* ../../docs_src/handling_errors/tutorial005_py39.py hl[14] *}
Теперь попробуйте отправить недействительный элемент, например:
Если вы хотите использовать исключение вместе с теми же обработчиками исключений по умолчанию из **FastAPI**, вы можете импортировать и повторно использовать обработчики исключений по умолчанию из `fastapi.exception_handlers`:
-{* ../../docs_src/handling_errors/tutorial006.py hl[2:5,15,21] *}
+{* ../../docs_src/handling_errors/tutorial006_py39.py hl[2:5,15,21] *}
В этом примере вы просто `выводите в терминал` ошибку с очень выразительным сообщением, но идея вам понятна. Вы можете использовать исключение, а затем просто повторно использовать стандартные обработчики исключений.
Вы можете задать их следующим образом:
-{* ../../docs_src/metadata/tutorial001.py hl[3:16, 19:32] *}
+{* ../../docs_src/metadata/tutorial001_py39.py hl[3:16, 19:32] *}
/// tip | Подсказка
К примеру:
-{* ../../docs_src/metadata/tutorial001_1.py hl[31] *}
+{* ../../docs_src/metadata/tutorial001_1_py39.py hl[31] *}
## Метаданные для тегов { #metadata-for-tags }
Создайте метаданные для ваших тегов и передайте их в параметре `openapi_tags`:
-{* ../../docs_src/metadata/tutorial004.py hl[3:16,18] *}
+{* ../../docs_src/metadata/tutorial004_py39.py hl[3:16,18] *}
Помните, что вы можете использовать Markdown внутри описания, к примеру "login" будет отображен жирным шрифтом (**login**) и "fancy" будет отображаться курсивом (_fancy_).
Используйте параметр `tags` с вашими *операциями пути* (и `APIRouter`ами), чтобы присвоить им различные теги:
-{* ../../docs_src/metadata/tutorial004.py hl[21,26] *}
+{* ../../docs_src/metadata/tutorial004_py39.py hl[21,26] *}
/// info | Дополнительная информация
К примеру, чтобы задать её отображение по адресу `/api/v1/openapi.json`:
-{* ../../docs_src/metadata/tutorial002.py hl[3] *}
+{* ../../docs_src/metadata/tutorial002_py39.py hl[3] *}
Если вы хотите отключить схему OpenAPI полностью, вы можете задать `openapi_url=None`, это также отключит пользовательские интерфейсы документации, которые её используют.
К примеру, чтобы задать отображение Swagger UI по адресу `/documentation` и отключить ReDoc:
-{* ../../docs_src/metadata/tutorial003.py hl[3] *}
+{* ../../docs_src/metadata/tutorial003_py39.py hl[3] *}
* Затем она возвращает ответ `response`, сгенерированный *операцией пути*.
* Также имеется возможность видоизменить `response`, перед тем как его вернуть.
-{* ../../docs_src/middleware/tutorial001.py hl[8:9,11,14] *}
+{* ../../docs_src/middleware/tutorial001_py39.py hl[8:9,11,14] *}
/// tip | Примечание
Например, вы можете добавить собственный заголовок `X-Process-Time`, содержащий время в секундах, необходимое для обработки запроса и генерации ответа:
-{* ../../docs_src/middleware/tutorial001.py hl[10,12:13] *}
+{* ../../docs_src/middleware/tutorial001_py39.py hl[10,12:13] *}
/// tip | Примечание
**FastAPI** поддерживает это так же, как и в случае с обычными строками:
-{* ../../docs_src/path_operation_configuration/tutorial002b.py hl[1,8:10,13,18] *}
+{* ../../docs_src/path_operation_configuration/tutorial002b_py39.py hl[1,8:10,13,18] *}
## Краткое и развёрнутое содержание { #summary-and-description }
Если вам необходимо пометить *операцию пути* как <abbr title="устаревшее, не рекомендовано к использованию">устаревшую</abbr>, при этом не удаляя её, передайте параметр `deprecated`:
-{* ../../docs_src/path_operation_configuration/tutorial006.py hl[16] *}
+{* ../../docs_src/path_operation_configuration/tutorial006_py39.py hl[16] *}
Он будет четко помечен как устаревший в интерактивной документации:
Поэтому вы можете определить функцию так:
-{* ../../docs_src/path_params_numeric_validations/tutorial002.py hl[7] *}
+{* ../../docs_src/path_params_numeric_validations/tutorial002_py39.py hl[7] *}
Но имейте в виду, что если вы используете `Annotated`, вы не столкнётесь с этой проблемой, так как вы не используете значения по умолчанию параметров функции для `Query()` или `Path()`.
Python не будет ничего делать с `*`, но он будет знать, что все следующие параметры являются именованными аргументами (парами ключ-значение), также известными как <abbr title="От: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>, даже если у них нет значений по умолчанию.
-{* ../../docs_src/path_params_numeric_validations/tutorial003.py hl[7] *}
+{* ../../docs_src/path_params_numeric_validations/tutorial003_py39.py hl[7] *}
### Лучше с `Annotated` { #better-with-annotated }
Вы можете определить "параметры" или "переменные" пути, используя синтаксис форматированных строк Python:
-{* ../../docs_src/path_params/tutorial001.py hl[6:7] *}
+{* ../../docs_src/path_params/tutorial001_py39.py hl[6:7] *}
Значение параметра пути `item_id` будет передано в функцию в качестве аргумента `item_id`.
Вы можете объявить тип параметра пути в функции, используя стандартные аннотации типов Python:
-{* ../../docs_src/path_params/tutorial002.py hl[7] *}
+{* ../../docs_src/path_params/tutorial002_py39.py hl[7] *}
Здесь, `item_id` объявлен типом `int`.
Поскольку *операции пути* выполняются в порядке их объявления, необходимо, чтобы путь для `/users/me` был объявлен раньше, чем путь для `/users/{user_id}`:
-{* ../../docs_src/path_params/tutorial003.py hl[6,11] *}
+{* ../../docs_src/path_params/tutorial003_py39.py hl[6,11] *}
Иначе путь для `/users/{user_id}` также будет соответствовать `/users/me`, "подразумевая", что он получает параметр `user_id` со значением `"me"`.
Аналогично, вы не можете переопределить операцию с путем:
-{* ../../docs_src/path_params/tutorial003b.py hl[6,11] *}
+{* ../../docs_src/path_params/tutorial003b_py39.py hl[6,11] *}
Первый будет выполняться всегда, так как путь совпадает первым.
Затем создайте атрибуты класса с фиксированными допустимыми значениями:
-{* ../../docs_src/path_params/tutorial005.py hl[1,6:9] *}
-
-/// info | Дополнительная информация
-
-<a href="https://docs.python.org/3/library/enum.html" class="external-link" target="_blank">Перечисления (enum) доступны в Python</a> начиная с версии 3.4.
-
-///
+{* ../../docs_src/path_params/tutorial005_py39.py hl[1,6:9] *}
/// tip | Подсказка
Определите *параметр пути*, используя в аннотации типа класс перечисления (`ModelName`), созданный ранее:
-{* ../../docs_src/path_params/tutorial005.py hl[16] *}
+{* ../../docs_src/path_params/tutorial005_py39.py hl[16] *}
### Проверьте документацию { #check-the-docs }
Вы можете сравнить это значение с *элементом перечисления* класса `ModelName`:
-{* ../../docs_src/path_params/tutorial005.py hl[17] *}
+{* ../../docs_src/path_params/tutorial005_py39.py hl[17] *}
#### Получение *значения перечисления* { #get-the-enumeration-value }
Можно получить фактическое значение (в данном случае - `str`) с помощью `model_name.value` или в общем случае `your_enum_member.value`:
-{* ../../docs_src/path_params/tutorial005.py hl[20] *}
+{* ../../docs_src/path_params/tutorial005_py39.py hl[20] *}
/// tip | Подсказка
Они будут преобразованы в соответствующие значения (в данном случае - строки) перед их возвратом клиенту:
-{* ../../docs_src/path_params/tutorial005.py hl[18,21,23] *}
+{* ../../docs_src/path_params/tutorial005_py39.py hl[18,21,23] *}
Вы отправите клиенту такой JSON-ответ:
```JSON
Можете использовать так:
-{* ../../docs_src/path_params/tutorial004.py hl[6] *}
+{* ../../docs_src/path_params/tutorial004_py39.py hl[6] *}
/// tip | Подсказка
////
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
q: Union[str, None] = None
////
-//// tab | Python 3.8+
+//// tab | Python 3.9+
```Python
q: Annotated[Union[str, None]] = None
Когда вы объявляете параметры функции, которые не являются параметрами пути, они автоматически интерпретируются как "query"-параметры.
-{* ../../docs_src/query_params/tutorial001.py hl[9] *}
+{* ../../docs_src/query_params/tutorial001_py39.py hl[9] *}
Query-параметры представляют из себя набор пар ключ-значение, которые идут после знака `?` в URL-адресе, разделенные символами `&`.
Но если вы хотите сделать query-параметр обязательным, вы можете просто не указывать значение по умолчанию:
-{* ../../docs_src/query_params/tutorial005.py hl[6:7] *}
+{* ../../docs_src/query_params/tutorial005_py39.py hl[6:7] *}
Здесь параметр запроса `needy` является обязательным параметром с типом данных `str`.
Самый распространённый случай — [возвращать Response напрямую, как описано далее в разделах для продвинутых](../advanced/response-directly.md){.internal-link target=_blank}.
-{* ../../docs_src/response_model/tutorial003_02.py hl[8,10:11] *}
+{* ../../docs_src/response_model/tutorial003_02_py39.py hl[8,10:11] *}
Этот простой случай обрабатывается FastAPI автоматически, потому что аннотация возвращаемого типа — это класс (или подкласс) `Response`.
Вы также можете использовать подкласс `Response` в аннотации типа:
-{* ../../docs_src/response_model/tutorial003_03.py hl[8:9] *}
+{* ../../docs_src/response_model/tutorial003_03_py39.py hl[8:9] *}
Это тоже сработает, так как `RedirectResponse` — подкласс `Response`, и FastAPI автоматически обработает этот случай.
* `@app.delete()`
* и других.
-{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
+{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
/// note | Примечание
Рассмотрим предыдущий пример еще раз:
-{* ../../docs_src/response_status_code/tutorial001.py hl[6] *}
+{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
`201` – это код статуса "Создано".
Для удобства вы можете использовать переменные из `fastapi.status`.
-{* ../../docs_src/response_status_code/tutorial002.py hl[1,6] *}
+{* ../../docs_src/response_status_code/tutorial002_py39.py hl[1,6] *}
Они содержат те же числовые значения, но позволяют использовать автозавершение редактора кода для выбора кода статуса:
* Импортируйте `StaticFiles`.
* "Примонтируйте" экземпляр `StaticFiles()` к определённому пути.
-{* ../../docs_src/static_files/tutorial001.py hl[2,6] *}
+{* ../../docs_src/static_files/tutorial001_py39.py hl[2,6] *}
/// note | Технические детали
Напишите простое утверждение с `assert` дабы проверить истинность Python-выражения (это тоже стандарт `pytest`).
-{* ../../docs_src/app_testing/tutorial001.py hl[2,12,15:18] *}
+{* ../../docs_src/app_testing/tutorial001_py39.py hl[2,12,15:18] *}
/// tip | Подсказка
В файле `main.py` находится Ваше приложение **FastAPI**:
-{* ../../docs_src/app_testing/main.py *}
+{* ../../docs_src/app_testing/app_a_py39/main.py *}
### Файл тестов { #testing-file }
Так как оба файла находятся в одной директории, для импорта объекта приложения из файла `main` в файл `test_main` Вы можете использовать относительный импорт:
-{* ../../docs_src/app_testing/test_main.py hl[3] *}
+{* ../../docs_src/app_testing/app_a_py39/test_main.py hl[3] *}
...и писать дальше тесты, как и раньше.
+++ /dev/null
-from typing import Union
-
-from fastapi import Body, FastAPI, status
-from fastapi.responses import JSONResponse
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-items = {"foo": {"name": "Fighters", "size": 6}, "bar": {"name": "Tenders", "size": 3}}
-
-
-@app.put("/items/{item_id}")
-async def upsert_item(
- item_id: str,
- name: Annotated[Union[str, None], Body()] = None,
- size: Annotated[Union[int, None], Body()] = None,
-):
- if item_id in items:
- item = items[item_id]
- item["name"] = name
- item["size"] = size
- return item
- else:
- item = {"name": name, "size": size}
- items[item_id] = item
- return JSONResponse(status_code=status.HTTP_201_CREATED, content=item)
+++ /dev/null
-from typing import Union
-
-from fastapi import FastAPI, Header, HTTPException
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-fake_secret_token = "coneofsilence"
-
-fake_db = {
- "foo": {"id": "foo", "title": "Foo", "description": "There goes my hero"},
- "bar": {"id": "bar", "title": "Bar", "description": "The bartenders"},
-}
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- id: str
- title: str
- description: Union[str, None] = None
-
-
-@app.get("/items/{item_id}", response_model=Item)
-async def read_main(item_id: str, x_token: Annotated[str, Header()]):
- if x_token != fake_secret_token:
- raise HTTPException(status_code=400, detail="Invalid X-Token header")
- if item_id not in fake_db:
- raise HTTPException(status_code=404, detail="Item not found")
- return fake_db[item_id]
-
-
-@app.post("/items/", response_model=Item)
-async def create_item(item: Item, x_token: Annotated[str, Header()]):
- if x_token != fake_secret_token:
- raise HTTPException(status_code=400, detail="Invalid X-Token header")
- if item.id in fake_db:
- raise HTTPException(status_code=409, detail="Item already exists")
- fake_db[item.id] = item
- return item
+++ /dev/null
-from fastapi.testclient import TestClient
-
-from .main import app
-
-client = TestClient(app)
-
-
-def test_read_item():
- response = client.get("/items/foo", headers={"X-Token": "coneofsilence"})
- assert response.status_code == 200
- assert response.json() == {
- "id": "foo",
- "title": "Foo",
- "description": "There goes my hero",
- }
-
-
-def test_read_item_bad_token():
- response = client.get("/items/foo", headers={"X-Token": "hailhydra"})
- assert response.status_code == 400
- assert response.json() == {"detail": "Invalid X-Token header"}
-
-
-def test_read_nonexistent_item():
- response = client.get("/items/baz", headers={"X-Token": "coneofsilence"})
- assert response.status_code == 404
- assert response.json() == {"detail": "Item not found"}
-
-
-def test_create_item():
- response = client.post(
- "/items/",
- headers={"X-Token": "coneofsilence"},
- json={"id": "foobar", "title": "Foo Bar", "description": "The Foo Barters"},
- )
- assert response.status_code == 200
- assert response.json() == {
- "id": "foobar",
- "title": "Foo Bar",
- "description": "The Foo Barters",
- }
-
-
-def test_create_item_bad_token():
- response = client.post(
- "/items/",
- headers={"X-Token": "hailhydra"},
- json={"id": "bazz", "title": "Bazz", "description": "Drop the bazz"},
- )
- assert response.status_code == 400
- assert response.json() == {"detail": "Invalid X-Token header"}
-
-
-def test_create_existing_item():
- response = client.post(
- "/items/",
- headers={"X-Token": "coneofsilence"},
- json={
- "id": "foo",
- "title": "The Foo ID Stealers",
- "description": "There goes my stealer",
- },
- )
- assert response.status_code == 409
- assert response.json() == {"detail": "Item already exists"}
+++ /dev/null
-from fastapi import Depends, FastAPI, HTTPException, status
-from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class HTTPBearer403(HTTPBearer):
- def make_not_authenticated_error(self) -> HTTPException:
- return HTTPException(
- status_code=status.HTTP_403_FORBIDDEN, detail="Not authenticated"
- )
-
-
-CredentialsDep = Annotated[HTTPAuthorizationCredentials, Depends(HTTPBearer403())]
-
-
-@app.get("/me")
-def read_me(credentials: CredentialsDep):
- return {"message": "You are authenticated", "token": credentials.credentials}
+++ /dev/null
-from typing import Union
-
-from fastapi import BackgroundTasks, Depends, FastAPI
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-def write_log(message: str):
- with open("log.txt", mode="a") as log:
- log.write(message)
-
-
-def get_query(background_tasks: BackgroundTasks, q: Union[str, None] = None):
- if q:
- message = f"found query: {q}\n"
- background_tasks.add_task(write_log, message)
- return q
-
-
-@app.post("/send-notification/{email}")
-async def send_notification(
- email: str, background_tasks: BackgroundTasks, q: Annotated[str, Depends(get_query)]
-):
- message = f"message to {email}\n"
- background_tasks.add_task(write_log, message)
- return {"message": "Message sent"}
+++ /dev/null
-from fastapi import Header, HTTPException
-from typing_extensions import Annotated
-
-
-async def get_token_header(x_token: Annotated[str, Header()]):
- if x_token != "fake-super-secret-token":
- raise HTTPException(status_code=400, detail="X-Token header invalid")
-
-
-async def get_query_token(token: str):
- if token != "jessica":
- raise HTTPException(status_code=400, detail="No Jessica token provided")
+++ /dev/null
-from fastapi import APIRouter
-
-router = APIRouter()
-
-
-@router.post("/")
-async def update_admin():
- return {"message": "Admin getting schwifty"}
+++ /dev/null
-from fastapi import Depends, FastAPI
-
-from .dependencies import get_query_token, get_token_header
-from .internal import admin
-from .routers import items, users
-
-app = FastAPI(dependencies=[Depends(get_query_token)])
-
-
-app.include_router(users.router)
-app.include_router(items.router)
-app.include_router(
- admin.router,
- prefix="/admin",
- tags=["admin"],
- dependencies=[Depends(get_token_header)],
- responses={418: {"description": "I'm a teapot"}},
-)
-
-
-@app.get("/")
-async def root():
- return {"message": "Hello Bigger Applications!"}
+++ /dev/null
-from fastapi import APIRouter, Depends, HTTPException
-
-from ..dependencies import get_token_header
-
-router = APIRouter(
- prefix="/items",
- tags=["items"],
- dependencies=[Depends(get_token_header)],
- responses={404: {"description": "Not found"}},
-)
-
-
-fake_items_db = {"plumbus": {"name": "Plumbus"}, "gun": {"name": "Portal Gun"}}
-
-
-@router.get("/")
-async def read_items():
- return fake_items_db
-
-
-@router.get("/{item_id}")
-async def read_item(item_id: str):
- if item_id not in fake_items_db:
- raise HTTPException(status_code=404, detail="Item not found")
- return {"name": fake_items_db[item_id]["name"], "item_id": item_id}
-
-
-@router.put(
- "/{item_id}",
- tags=["custom"],
- responses={403: {"description": "Operation forbidden"}},
-)
-async def update_item(item_id: str):
- if item_id != "plumbus":
- raise HTTPException(
- status_code=403, detail="You can only update the item: plumbus"
- )
- return {"item_id": item_id, "name": "The great Plumbus"}
+++ /dev/null
-from fastapi import APIRouter
-
-router = APIRouter()
-
-
-@router.get("/users/", tags=["users"])
-async def read_users():
- return [{"username": "Rick"}, {"username": "Morty"}]
-
-
-@router.get("/users/me", tags=["users"])
-async def read_user_me():
- return {"username": "fakecurrentuser"}
-
-
-@router.get("/users/{username}", tags=["users"])
-async def read_user(username: str):
- return {"username": username}
+++ /dev/null
-from typing import Union
-
-from fastapi import Body, FastAPI
-from pydantic import BaseModel, Field
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = Field(
- default=None, title="The description of the item", max_length=300
- )
- price: float = Field(gt=0, description="The price must be greater than zero")
- tax: Union[float, None] = None
-
-
-@app.put("/items/{item_id}")
-async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
- results = {"item_id": item_id, "item": item}
- return results
+++ /dev/null
-from typing import Union
-
-from fastapi import FastAPI, Path
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- tax: Union[float, None] = None
-
-
-@app.put("/items/{item_id}")
-async def update_item(
- item_id: Annotated[int, Path(title="The ID of the item to get", ge=0, le=1000)],
- q: Union[str, None] = None,
- item: Union[Item, None] = None,
-):
- results = {"item_id": item_id}
- if q:
- results.update({"q": q})
- if item:
- results.update({"item": item})
- return results
+++ /dev/null
-from typing import Union
-
-from fastapi import Body, FastAPI
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- tax: Union[float, None] = None
-
-
-class User(BaseModel):
- username: str
- full_name: Union[str, None] = None
-
-
-@app.put("/items/{item_id}")
-async def update_item(
- item_id: int, item: Item, user: User, importance: Annotated[int, Body()]
-):
- results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
- return results
+++ /dev/null
-from typing import Union
-
-from fastapi import Body, FastAPI
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- tax: Union[float, None] = None
-
-
-class User(BaseModel):
- username: str
- full_name: Union[str, None] = None
-
-
-@app.put("/items/{item_id}")
-async def update_item(
- *,
- item_id: int,
- item: Item,
- user: User,
- importance: Annotated[int, Body(gt=0)],
- q: Union[str, None] = None,
-):
- results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
- if q:
- results.update({"q": q})
- return results
+++ /dev/null
-from typing import Union
-
-from fastapi import Body, FastAPI
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- tax: Union[float, None] = None
-
-
-@app.put("/items/{item_id}")
-async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
- results = {"item_id": item_id, "item": item}
- return results
+++ /dev/null
-from typing import List, Union
-
-from fastapi import FastAPI
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- tax: Union[float, None] = None
- tags: List[str] = []
-
-
-@app.put("/items/{item_id}")
-async def update_item(item_id: int, item: Item):
- results = {"item_id": item_id, "item": item}
- return results
+++ /dev/null
-from typing import Set, Union
-
-from fastapi import FastAPI
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- tax: Union[float, None] = None
- tags: Set[str] = set()
-
-
-@app.put("/items/{item_id}")
-async def update_item(item_id: int, item: Item):
- results = {"item_id": item_id, "item": item}
- return results
+++ /dev/null
-from typing import Set, Union
-
-from fastapi import FastAPI
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class Image(BaseModel):
- url: str
- name: str
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- tax: Union[float, None] = None
- tags: Set[str] = set()
- image: Union[Image, None] = None
-
-
-@app.put("/items/{item_id}")
-async def update_item(item_id: int, item: Item):
- results = {"item_id": item_id, "item": item}
- return results
+++ /dev/null
-from typing import Set, Union
-
-from fastapi import FastAPI
-from pydantic import BaseModel, HttpUrl
-
-app = FastAPI()
-
-
-class Image(BaseModel):
- url: HttpUrl
- name: str
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- tax: Union[float, None] = None
- tags: Set[str] = set()
- image: Union[Image, None] = None
-
-
-@app.put("/items/{item_id}")
-async def update_item(item_id: int, item: Item):
- results = {"item_id": item_id, "item": item}
- return results
+++ /dev/null
-from typing import List, Set, Union
-
-from fastapi import FastAPI
-from pydantic import BaseModel, HttpUrl
-
-app = FastAPI()
-
-
-class Image(BaseModel):
- url: HttpUrl
- name: str
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- tax: Union[float, None] = None
- tags: Set[str] = set()
- images: Union[List[Image], None] = None
-
-
-@app.put("/items/{item_id}")
-async def update_item(item_id: int, item: Item):
- results = {"item_id": item_id, "item": item}
- return results
+++ /dev/null
-from typing import List, Set, Union
-
-from fastapi import FastAPI
-from pydantic import BaseModel, HttpUrl
-
-app = FastAPI()
-
-
-class Image(BaseModel):
- url: HttpUrl
- name: str
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- tax: Union[float, None] = None
- tags: Set[str] = set()
- images: Union[List[Image], None] = None
-
-
-class Offer(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- items: List[Item]
-
-
-@app.post("/offers/")
-async def create_offer(offer: Offer):
- return offer
+++ /dev/null
-from typing import List
-
-from fastapi import FastAPI
-from pydantic import BaseModel, HttpUrl
-
-app = FastAPI()
-
-
-class Image(BaseModel):
- url: HttpUrl
- name: str
-
-
-@app.post("/images/multiple/")
-async def create_multiple_images(images: List[Image]):
- return images
+++ /dev/null
-from typing import Dict
-
-from fastapi import FastAPI
-
-app = FastAPI()
-
-
-@app.post("/index-weights/")
-async def create_index_weights(weights: Dict[int, float]):
- return weights
+++ /dev/null
-from typing import List, Union
-
-from fastapi import FastAPI
-from fastapi.encoders import jsonable_encoder
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: Union[str, None] = None
- description: Union[str, None] = None
- price: Union[float, None] = None
- tax: float = 10.5
- tags: List[str] = []
-
-
-items = {
- "foo": {"name": "Foo", "price": 50.2},
- "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
- "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
-}
-
-
-@app.get("/items/{item_id}", response_model=Item)
-async def read_item(item_id: str):
- return items[item_id]
-
-
-@app.put("/items/{item_id}", response_model=Item)
-async def update_item(item_id: str, item: Item):
- update_item_encoded = jsonable_encoder(item)
- items[item_id] = update_item_encoded
- return update_item_encoded
+++ /dev/null
-from typing import List, Union
-
-from fastapi import FastAPI
-from fastapi.encoders import jsonable_encoder
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: Union[str, None] = None
- description: Union[str, None] = None
- price: Union[float, None] = None
- tax: float = 10.5
- tags: List[str] = []
-
-
-items = {
- "foo": {"name": "Foo", "price": 50.2},
- "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
- "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
-}
-
-
-@app.get("/items/{item_id}", response_model=Item)
-async def read_item(item_id: str):
- return items[item_id]
-
-
-@app.patch("/items/{item_id}", response_model=Item)
-async def update_item(item_id: str, item: Item):
- stored_item_data = items[item_id]
- stored_item_model = Item(**stored_item_data)
- update_data = item.dict(exclude_unset=True)
- updated_item = stored_item_model.copy(update=update_data)
- items[item_id] = jsonable_encoder(updated_item)
- return updated_item
+++ /dev/null
-from typing import Union
-
-from fastapi import Cookie, FastAPI
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class Cookies(BaseModel):
- session_id: str
- fatebook_tracker: Union[str, None] = None
- googall_tracker: Union[str, None] = None
-
-
-@app.get("/items/")
-async def read_items(cookies: Annotated[Cookies, Cookie()]):
- return cookies
+++ /dev/null
-from typing import Union
-
-from fastapi import Cookie, FastAPI
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class Cookies(BaseModel):
- model_config = {"extra": "forbid"}
-
- session_id: str
- fatebook_tracker: Union[str, None] = None
- googall_tracker: Union[str, None] = None
-
-
-@app.get("/items/")
-async def read_items(cookies: Annotated[Cookies, Cookie()]):
- return cookies
+++ /dev/null
-from typing import Union
-
-from fastapi import Cookie, FastAPI
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class Cookies(BaseModel):
- class Config:
- extra = "forbid"
-
- session_id: str
- fatebook_tracker: Union[str, None] = None
- googall_tracker: Union[str, None] = None
-
-
-@app.get("/items/")
-async def read_items(cookies: Annotated[Cookies, Cookie()]):
- return cookies
+++ /dev/null
-from typing import Union
-
-from fastapi import Cookie, FastAPI
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-@app.get("/items/")
-async def read_items(ads_id: Annotated[Union[str, None], Cookie()] = None):
- return {"ads_id": ads_id}
+++ /dev/null
-import gzip
-from typing import Callable, List
-
-from fastapi import Body, FastAPI, Request, Response
-from fastapi.routing import APIRoute
-
-
-class GzipRequest(Request):
- async def body(self) -> bytes:
- if not hasattr(self, "_body"):
- body = await super().body()
- if "gzip" in self.headers.getlist("Content-Encoding"):
- body = gzip.decompress(body)
- self._body = body
- return self._body
-
-
-class GzipRoute(APIRoute):
- def get_route_handler(self) -> Callable:
- original_route_handler = super().get_route_handler()
-
- async def custom_route_handler(request: Request) -> Response:
- request = GzipRequest(request.scope, request.receive)
- return await original_route_handler(request)
-
- return custom_route_handler
-
-
-app = FastAPI()
-app.router.route_class = GzipRoute
-
-
-@app.post("/sum")
-async def sum_numbers(numbers: List[int] = Body()):
- return {"sum": sum(numbers)}
+++ /dev/null
-import gzip
-from typing import Callable, List
-
-from fastapi import Body, FastAPI, Request, Response
-from fastapi.routing import APIRoute
-from typing_extensions import Annotated
-
-
-class GzipRequest(Request):
- async def body(self) -> bytes:
- if not hasattr(self, "_body"):
- body = await super().body()
- if "gzip" in self.headers.getlist("Content-Encoding"):
- body = gzip.decompress(body)
- self._body = body
- return self._body
-
-
-class GzipRoute(APIRoute):
- def get_route_handler(self) -> Callable:
- original_route_handler = super().get_route_handler()
-
- async def custom_route_handler(request: Request) -> Response:
- request = GzipRequest(request.scope, request.receive)
- return await original_route_handler(request)
-
- return custom_route_handler
-
-
-app = FastAPI()
-app.router.route_class = GzipRoute
-
-
-@app.post("/sum")
-async def sum_numbers(numbers: Annotated[List[int], Body()]):
- return {"sum": sum(numbers)}
+++ /dev/null
-from typing import Callable, List
-
-from fastapi import Body, FastAPI, HTTPException, Request, Response
-from fastapi.exceptions import RequestValidationError
-from fastapi.routing import APIRoute
-
-
-class ValidationErrorLoggingRoute(APIRoute):
- def get_route_handler(self) -> Callable:
- original_route_handler = super().get_route_handler()
-
- async def custom_route_handler(request: Request) -> Response:
- try:
- return await original_route_handler(request)
- except RequestValidationError as exc:
- body = await request.body()
- detail = {"errors": exc.errors(), "body": body.decode()}
- raise HTTPException(status_code=422, detail=detail)
-
- return custom_route_handler
-
-
-app = FastAPI()
-app.router.route_class = ValidationErrorLoggingRoute
-
-
-@app.post("/")
-async def sum_numbers(numbers: List[int] = Body()):
- return sum(numbers)
+++ /dev/null
-from typing import Callable, List
-
-from fastapi import Body, FastAPI, HTTPException, Request, Response
-from fastapi.exceptions import RequestValidationError
-from fastapi.routing import APIRoute
-from typing_extensions import Annotated
-
-
-class ValidationErrorLoggingRoute(APIRoute):
- def get_route_handler(self) -> Callable:
- original_route_handler = super().get_route_handler()
-
- async def custom_route_handler(request: Request) -> Response:
- try:
- return await original_route_handler(request)
- except RequestValidationError as exc:
- body = await request.body()
- detail = {"errors": exc.errors(), "body": body.decode()}
- raise HTTPException(status_code=422, detail=detail)
-
- return custom_route_handler
-
-
-app = FastAPI()
-app.router.route_class = ValidationErrorLoggingRoute
-
-
-@app.post("/")
-async def sum_numbers(numbers: Annotated[List[int], Body()]):
- return sum(numbers)
+++ /dev/null
-from dataclasses import dataclass, field
-from typing import List, Union
-
-from fastapi import FastAPI
-
-
-@dataclass
-class Item:
- name: str
- price: float
- tags: List[str] = field(default_factory=list)
- description: Union[str, None] = None
- tax: Union[float, None] = None
-
-
-app = FastAPI()
-
-
-@app.get("/items/next", response_model=Item)
-async def read_next_item():
- return {
- "name": "Island In The Moon",
- "price": 12.99,
- "description": "A place to be playin' and havin' fun",
- "tags": ["breater"],
- }
+++ /dev/null
-from dataclasses import field # (1)
-from typing import List, Union
-
-from fastapi import FastAPI
-from pydantic.dataclasses import dataclass # (2)
-
-
-@dataclass
-class Item:
- name: str
- description: Union[str, None] = None
-
-
-@dataclass
-class Author:
- name: str
- items: List[Item] = field(default_factory=list) # (3)
-
-
-app = FastAPI()
-
-
-@app.post("/authors/{author_id}/items/", response_model=Author) # (4)
-async def create_author_items(author_id: str, items: List[Item]): # (5)
- return {"name": author_id, "items": items} # (6)
-
-
-@app.get("/authors/", response_model=List[Author]) # (7)
-def get_authors(): # (8)
- return [ # (9)
- {
- "name": "Breaters",
- "items": [
- {
- "name": "Island In The Moon",
- "description": "A place to be playin' and havin' fun",
- },
- {"name": "Holy Buddies"},
- ],
- },
- {
- "name": "System of an Up",
- "items": [
- {
- "name": "Salt",
- "description": "The kombucha mushroom people's favorite",
- },
- {"name": "Pad Thai"},
- {
- "name": "Lonely Night",
- "description": "The mostests lonliest nightiest of allest",
- },
- ],
- },
- ]
+++ /dev/null
-from typing import Union
-
-from fastapi import Depends, FastAPI
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-async def common_parameters(
- q: Union[str, None] = None, skip: int = 0, limit: int = 100
-):
- return {"q": q, "skip": skip, "limit": limit}
-
-
-CommonsDep = Annotated[dict, Depends(common_parameters)]
-
-
-@app.get("/items/")
-async def read_items(commons: CommonsDep):
- return commons
-
-
-@app.get("/users/")
-async def read_users(commons: CommonsDep):
- return commons
+++ /dev/null
-from typing import Union
-
-from fastapi import Depends, FastAPI
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-async def common_parameters(
- q: Union[str, None] = None, skip: int = 0, limit: int = 100
-):
- return {"q": q, "skip": skip, "limit": limit}
-
-
-@app.get("/items/")
-async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
- return commons
-
-
-@app.get("/users/")
-async def read_users(commons: Annotated[dict, Depends(common_parameters)]):
- return commons
+++ /dev/null
-from typing import Union
-
-from fastapi import Depends, FastAPI
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
-
-
-class CommonQueryParams:
- def __init__(self, q: Union[str, None] = None, skip: int = 0, limit: int = 100):
- self.q = q
- self.skip = skip
- self.limit = limit
-
-
-@app.get("/items/")
-async def read_items(commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]):
- response = {}
- if commons.q:
- response.update({"q": commons.q})
- items = fake_items_db[commons.skip : commons.skip + commons.limit]
- response.update({"items": items})
- return response
+++ /dev/null
-from typing import Any, Union
-
-from fastapi import Depends, FastAPI
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
-
-
-class CommonQueryParams:
- def __init__(self, q: Union[str, None] = None, skip: int = 0, limit: int = 100):
- self.q = q
- self.skip = skip
- self.limit = limit
-
-
-@app.get("/items/")
-async def read_items(commons: Annotated[Any, Depends(CommonQueryParams)]):
- response = {}
- if commons.q:
- response.update({"q": commons.q})
- items = fake_items_db[commons.skip : commons.skip + commons.limit]
- response.update({"items": items})
- return response
+++ /dev/null
-from typing import Union
-
-from fastapi import Depends, FastAPI
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
-
-
-class CommonQueryParams:
- def __init__(self, q: Union[str, None] = None, skip: int = 0, limit: int = 100):
- self.q = q
- self.skip = skip
- self.limit = limit
-
-
-@app.get("/items/")
-async def read_items(commons: Annotated[CommonQueryParams, Depends()]):
- response = {}
- if commons.q:
- response.update({"q": commons.q})
- items = fake_items_db[commons.skip : commons.skip + commons.limit]
- response.update({"items": items})
- return response
+++ /dev/null
-from typing import Union
-
-from fastapi import Cookie, Depends, FastAPI
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-def query_extractor(q: Union[str, None] = None):
- return q
-
-
-def query_or_cookie_extractor(
- q: Annotated[str, Depends(query_extractor)],
- last_query: Annotated[Union[str, None], Cookie()] = None,
-):
- if not q:
- return last_query
- return q
-
-
-@app.get("/items/")
-async def read_query(
- query_or_default: Annotated[str, Depends(query_or_cookie_extractor)],
-):
- return {"q_or_cookie": query_or_default}
+++ /dev/null
-from fastapi import Depends, FastAPI, Header, HTTPException
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-async def verify_token(x_token: Annotated[str, Header()]):
- if x_token != "fake-super-secret-token":
- raise HTTPException(status_code=400, detail="X-Token header invalid")
-
-
-async def verify_key(x_key: Annotated[str, Header()]):
- if x_key != "fake-super-secret-key":
- raise HTTPException(status_code=400, detail="X-Key header invalid")
- return x_key
-
-
-@app.get("/items/", dependencies=[Depends(verify_token), Depends(verify_key)])
-async def read_items():
- return [{"item": "Foo"}, {"item": "Bar"}]
+++ /dev/null
-from fastapi import Depends
-from typing_extensions import Annotated
-
-
-async def dependency_a():
- dep_a = generate_dep_a()
- try:
- yield dep_a
- finally:
- dep_a.close()
-
-
-async def dependency_b(dep_a: Annotated[DepA, Depends(dependency_a)]):
- dep_b = generate_dep_b()
- try:
- yield dep_b
- finally:
- dep_b.close(dep_a)
-
-
-async def dependency_c(dep_b: Annotated[DepB, Depends(dependency_b)]):
- dep_c = generate_dep_c()
- try:
- yield dep_c
- finally:
- dep_c.close(dep_b)
+++ /dev/null
-from fastapi import Depends, FastAPI, HTTPException
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-data = {
- "plumbus": {"description": "Freshly pickled plumbus", "owner": "Morty"},
- "portal-gun": {"description": "Gun to create portals", "owner": "Rick"},
-}
-
-
-class OwnerError(Exception):
- pass
-
-
-def get_username():
- try:
- yield "Rick"
- except OwnerError as e:
- raise HTTPException(status_code=400, detail=f"Owner error: {e}")
-
-
-@app.get("/items/{item_id}")
-def get_item(item_id: str, username: Annotated[str, Depends(get_username)]):
- if item_id not in data:
- raise HTTPException(status_code=404, detail="Item not found")
- item = data[item_id]
- if item["owner"] != username:
- raise OwnerError(username)
- return item
+++ /dev/null
-from fastapi import Depends, FastAPI, HTTPException
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class InternalError(Exception):
- pass
-
-
-def get_username():
- try:
- yield "Rick"
- except InternalError:
- print("Oops, we didn't raise again, Britney 😱")
-
-
-@app.get("/items/{item_id}")
-def get_item(item_id: str, username: Annotated[str, Depends(get_username)]):
- if item_id == "portal-gun":
- raise InternalError(
- f"The portal gun is too dangerous to be owned by {username}"
- )
- if item_id != "plumbus":
- raise HTTPException(
- status_code=404, detail="Item not found, there's only a plumbus here"
- )
- return item_id
+++ /dev/null
-from fastapi import Depends, FastAPI, HTTPException
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class InternalError(Exception):
- pass
-
-
-def get_username():
- try:
- yield "Rick"
- except InternalError:
- print("We don't swallow the internal error here, we raise again 😎")
- raise
-
-
-@app.get("/items/{item_id}")
-def get_item(item_id: str, username: Annotated[str, Depends(get_username)]):
- if item_id == "portal-gun":
- raise InternalError(
- f"The portal gun is too dangerous to be owned by {username}"
- )
- if item_id != "plumbus":
- raise HTTPException(
- status_code=404, detail="Item not found, there's only a plumbus here"
- )
- return item_id
+++ /dev/null
-from fastapi import Depends, FastAPI
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-def get_username():
- try:
- yield "Rick"
- finally:
- print("Cleanup up before response is sent")
-
-
-@app.get("/users/me")
-def get_user_me(username: Annotated[str, Depends(get_username, scope="function")]):
- return username
+++ /dev/null
-from fastapi import Depends
-
-
-async def dependency_a():
- dep_a = generate_dep_a()
- try:
- yield dep_a
- finally:
- dep_a.close()
-
-
-async def dependency_b(dep_a=Depends(dependency_a)):
- dep_b = generate_dep_b()
- try:
- yield dep_b
- finally:
- dep_b.close(dep_a)
-
-
-async def dependency_c(dep_b=Depends(dependency_b)):
- dep_c = generate_dep_c()
- try:
- yield dep_c
- finally:
- dep_c.close(dep_b)
+++ /dev/null
-from fastapi import Depends, FastAPI
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class FixedContentQueryChecker:
- def __init__(self, fixed_content: str):
- self.fixed_content = fixed_content
-
- def __call__(self, q: str = ""):
- if q:
- return self.fixed_content in q
- return False
-
-
-checker = FixedContentQueryChecker("bar")
-
-
-@app.get("/query-checker/")
-async def read_query_check(fixed_content_included: Annotated[bool, Depends(checker)]):
- return {"fixed_content_in_query": fixed_content_included}
+++ /dev/null
-from fastapi import Depends, FastAPI, Header, HTTPException
-from typing_extensions import Annotated
-
-
-async def verify_token(x_token: Annotated[str, Header()]):
- if x_token != "fake-super-secret-token":
- raise HTTPException(status_code=400, detail="X-Token header invalid")
-
-
-async def verify_key(x_key: Annotated[str, Header()]):
- if x_key != "fake-super-secret-key":
- raise HTTPException(status_code=400, detail="X-Key header invalid")
- return x_key
-
-
-app = FastAPI(dependencies=[Depends(verify_token), Depends(verify_key)])
-
-
-@app.get("/items/")
-async def read_items():
- return [{"item": "Portal Gun"}, {"item": "Plumbus"}]
-
-
-@app.get("/users/")
-async def read_users():
- return [{"username": "Rick"}, {"username": "Morty"}]
+from typing import Annotated
+
from fastapi import Depends, FastAPI, Header, HTTPException
-from typing_extensions import Annotated
async def verify_token(x_token: Annotated[str, Header()]):
+++ /dev/null
-from typing import Union
-
-from fastapi import Depends, FastAPI
-from fastapi.testclient import TestClient
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-async def common_parameters(
- q: Union[str, None] = None, skip: int = 0, limit: int = 100
-):
- return {"q": q, "skip": skip, "limit": limit}
-
-
-@app.get("/items/")
-async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
- return {"message": "Hello Items!", "params": commons}
-
-
-@app.get("/users/")
-async def read_users(commons: Annotated[dict, Depends(common_parameters)]):
- return {"message": "Hello Users!", "params": commons}
-
-
-client = TestClient(app)
-
-
-async def override_dependency(q: Union[str, None] = None):
- return {"q": q, "skip": 5, "limit": 10}
-
-
-app.dependency_overrides[common_parameters] = override_dependency
-
-
-def test_override_in_items():
- response = client.get("/items/")
- assert response.status_code == 200
- assert response.json() == {
- "message": "Hello Items!",
- "params": {"q": None, "skip": 5, "limit": 10},
- }
-
-
-def test_override_in_items_with_q():
- response = client.get("/items/?q=foo")
- assert response.status_code == 200
- assert response.json() == {
- "message": "Hello Items!",
- "params": {"q": "foo", "skip": 5, "limit": 10},
- }
-
-
-def test_override_in_items_with_params():
- response = client.get("/items/?q=foo&skip=100&limit=200")
- assert response.status_code == 200
- assert response.json() == {
- "message": "Hello Items!",
- "params": {"q": "foo", "skip": 5, "limit": 10},
- }
+++ /dev/null
-from datetime import datetime, time, timedelta
-from typing import Union
-from uuid import UUID
-
-from fastapi import Body, FastAPI
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-@app.put("/items/{item_id}")
-async def read_items(
- item_id: UUID,
- start_datetime: Annotated[datetime, Body()],
- end_datetime: Annotated[datetime, Body()],
- process_after: Annotated[timedelta, Body()],
- repeat_at: Annotated[Union[time, None], Body()] = None,
-):
- start_process = start_datetime + process_after
- duration = end_datetime - start_process
- return {
- "item_id": item_id,
- "start_datetime": start_datetime,
- "end_datetime": end_datetime,
- "process_after": process_after,
- "repeat_at": repeat_at,
- "start_process": start_process,
- "duration": duration,
- }
+++ /dev/null
-from typing import List
-
-from fastapi import FastAPI
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- description: str
-
-
-items = [
- {"name": "Foo", "description": "There comes my hero"},
- {"name": "Red", "description": "It's my aeroplane"},
-]
-
-
-@app.get("/items/", response_model=List[Item])
-async def read_items():
- return items
+++ /dev/null
-from typing import Dict
-
-from fastapi import FastAPI
-
-app = FastAPI()
-
-
-@app.get("/keyword-weights/", response_model=Dict[str, float])
-async def read_keyword_weights():
- return {"foo": 2.3, "bar": 3.4}
+++ /dev/null
-from fastapi import FastAPI
-
-my_awesome_api = FastAPI()
-
-
-@my_awesome_api.get("/")
-async def root():
- return {"message": "Hello World"}
+++ /dev/null
-from typing import List
-
-from fastapi import FastAPI
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- price: float
-
-
-class ResponseMessage(BaseModel):
- message: str
-
-
-@app.post("/items/", response_model=ResponseMessage)
-async def create_item(item: Item):
- return {"message": "item received"}
-
-
-@app.get("/items/", response_model=List[Item])
-async def get_items():
- return [
- {"name": "Plumbus", "price": 3},
- {"name": "Portal Gun", "price": 9001},
- ]
+++ /dev/null
-from typing import List
-
-from fastapi import FastAPI
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- price: float
-
-
-class ResponseMessage(BaseModel):
- message: str
-
-
-class User(BaseModel):
- username: str
- email: str
-
-
-@app.post("/items/", response_model=ResponseMessage, tags=["items"])
-async def create_item(item: Item):
- return {"message": "Item received"}
-
-
-@app.get("/items/", response_model=List[Item], tags=["items"])
-async def get_items():
- return [
- {"name": "Plumbus", "price": 3},
- {"name": "Portal Gun", "price": 9001},
- ]
-
-
-@app.post("/users/", response_model=ResponseMessage, tags=["users"])
-async def create_user(user: User):
- return {"message": "User received"}
+++ /dev/null
-from typing import List
-
-from fastapi import FastAPI
-from fastapi.routing import APIRoute
-from pydantic import BaseModel
-
-
-def custom_generate_unique_id(route: APIRoute):
- return f"{route.tags[0]}-{route.name}"
-
-
-app = FastAPI(generate_unique_id_function=custom_generate_unique_id)
-
-
-class Item(BaseModel):
- name: str
- price: float
-
-
-class ResponseMessage(BaseModel):
- message: str
-
-
-class User(BaseModel):
- username: str
- email: str
-
-
-@app.post("/items/", response_model=ResponseMessage, tags=["items"])
-async def create_item(item: Item):
- return {"message": "Item received"}
-
-
-@app.get("/items/", response_model=List[Item], tags=["items"])
-async def get_items():
- return [
- {"name": "Plumbus", "price": 3},
- {"name": "Portal Gun", "price": 9001},
- ]
-
-
-@app.post("/users/", response_model=ResponseMessage, tags=["users"])
-async def create_user(user: User):
- return {"message": "User received"}
+++ /dev/null
-from typing import List, Union
-
-from fastapi import FastAPI, Header
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class CommonHeaders(BaseModel):
- host: str
- save_data: bool
- if_modified_since: Union[str, None] = None
- traceparent: Union[str, None] = None
- x_tag: List[str] = []
-
-
-@app.get("/items/")
-async def read_items(headers: CommonHeaders = Header()):
- return headers
+++ /dev/null
-from typing import List, Union
-
-from fastapi import FastAPI, Header
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class CommonHeaders(BaseModel):
- host: str
- save_data: bool
- if_modified_since: Union[str, None] = None
- traceparent: Union[str, None] = None
- x_tag: List[str] = []
-
-
-@app.get("/items/")
-async def read_items(headers: Annotated[CommonHeaders, Header()]):
- return headers
+++ /dev/null
-from typing import List, Union
-
-from fastapi import FastAPI, Header
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class CommonHeaders(BaseModel):
- model_config = {"extra": "forbid"}
-
- host: str
- save_data: bool
- if_modified_since: Union[str, None] = None
- traceparent: Union[str, None] = None
- x_tag: List[str] = []
-
-
-@app.get("/items/")
-async def read_items(headers: CommonHeaders = Header()):
- return headers
+++ /dev/null
-from typing import List, Union
-
-from fastapi import FastAPI, Header
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class CommonHeaders(BaseModel):
- model_config = {"extra": "forbid"}
-
- host: str
- save_data: bool
- if_modified_since: Union[str, None] = None
- traceparent: Union[str, None] = None
- x_tag: List[str] = []
-
-
-@app.get("/items/")
-async def read_items(headers: Annotated[CommonHeaders, Header()]):
- return headers
+++ /dev/null
-from typing import List, Union
-
-from fastapi import FastAPI, Header
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class CommonHeaders(BaseModel):
- class Config:
- extra = "forbid"
-
- host: str
- save_data: bool
- if_modified_since: Union[str, None] = None
- traceparent: Union[str, None] = None
- x_tag: List[str] = []
-
-
-@app.get("/items/")
-async def read_items(headers: CommonHeaders = Header()):
- return headers
+++ /dev/null
-from typing import List, Union
-
-from fastapi import FastAPI, Header
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class CommonHeaders(BaseModel):
- class Config:
- extra = "forbid"
-
- host: str
- save_data: bool
- if_modified_since: Union[str, None] = None
- traceparent: Union[str, None] = None
- x_tag: List[str] = []
-
-
-@app.get("/items/")
-async def read_items(headers: Annotated[CommonHeaders, Header()]):
- return headers
+++ /dev/null
-from typing import List, Union
-
-from fastapi import FastAPI, Header
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class CommonHeaders(BaseModel):
- host: str
- save_data: bool
- if_modified_since: Union[str, None] = None
- traceparent: Union[str, None] = None
- x_tag: List[str] = []
-
-
-@app.get("/items/")
-async def read_items(headers: CommonHeaders = Header(convert_underscores=False)):
- return headers
+++ /dev/null
-from typing import List, Union
-
-from fastapi import FastAPI, Header
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class CommonHeaders(BaseModel):
- host: str
- save_data: bool
- if_modified_since: Union[str, None] = None
- traceparent: Union[str, None] = None
- x_tag: List[str] = []
-
-
-@app.get("/items/")
-async def read_items(
- headers: Annotated[CommonHeaders, Header(convert_underscores=False)],
-):
- return headers
+++ /dev/null
-from typing import Union
-
-from fastapi import FastAPI, Header
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-@app.get("/items/")
-async def read_items(user_agent: Annotated[Union[str, None], Header()] = None):
- return {"User-Agent": user_agent}
+++ /dev/null
-from typing import Union
-
-from fastapi import FastAPI, Header
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-@app.get("/items/")
-async def read_items(
- strange_header: Annotated[
- Union[str, None], Header(convert_underscores=False)
- ] = None,
-):
- return {"strange_header": strange_header}
+++ /dev/null
-from typing import List, Union
-
-from fastapi import FastAPI, Header
-
-app = FastAPI()
-
-
-@app.get("/items/")
-async def read_items(x_token: Union[List[str], None] = Header(default=None)):
- return {"X-Token values": x_token}
+++ /dev/null
-from typing import List, Union
-
-from fastapi import FastAPI, Header
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-@app.get("/items/")
-async def read_items(x_token: Annotated[Union[List[str], None], Header()] = None):
- return {"X-Token values": x_token}
+++ /dev/null
-from typing import Set, Union
-
-from fastapi import FastAPI
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- tax: Union[float, None] = None
- tags: Set[str] = set()
-
-
-@app.post("/items/", response_model=Item, summary="Create an item")
-async def create_item(item: Item):
- """
- Create an item with all the information:
-
- - **name**: each item must have a name
- - **description**: a long description
- - **price**: required
- - **tax**: if the item doesn't have tax, you can omit this
- - **tags**: a set of unique tag strings for this item
- \f
- :param item: User input.
- """
- return item
+++ /dev/null
-from typing import List
-
-import yaml
-from fastapi import FastAPI, HTTPException, Request
-from pydantic import BaseModel, ValidationError
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- tags: List[str]
-
-
-@app.post(
- "/items/",
- openapi_extra={
- "requestBody": {
- "content": {"application/x-yaml": {"schema": Item.model_json_schema()}},
- "required": True,
- },
- },
-)
-async def create_item(request: Request):
- raw_body = await request.body()
- try:
- data = yaml.safe_load(raw_body)
- except yaml.YAMLError:
- raise HTTPException(status_code=422, detail="Invalid YAML")
- try:
- item = Item.model_validate(data)
- except ValidationError as e:
- raise HTTPException(status_code=422, detail=e.errors(include_url=False))
- return item
+++ /dev/null
-from typing import List
-
-import yaml
-from fastapi import FastAPI, HTTPException, Request
-from pydantic import BaseModel, ValidationError
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- tags: List[str]
-
-
-@app.post(
- "/items/",
- openapi_extra={
- "requestBody": {
- "content": {"application/x-yaml": {"schema": Item.schema()}},
- "required": True,
- },
- },
-)
-async def create_item(request: Request):
- raw_body = await request.body()
- try:
- data = yaml.safe_load(raw_body)
- except yaml.YAMLError:
- raise HTTPException(status_code=422, detail="Invalid YAML")
- try:
- item = Item.parse_obj(data)
- except ValidationError as e:
- raise HTTPException(status_code=422, detail=e.errors())
- return item
+++ /dev/null
-from typing import Set, Union
-
-from fastapi import FastAPI, status
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- tax: Union[float, None] = None
- tags: Set[str] = set()
-
-
-@app.post("/items/", response_model=Item, status_code=status.HTTP_201_CREATED)
-async def create_item(item: Item):
- return item
+++ /dev/null
-from typing import Set, Union
-
-from fastapi import FastAPI
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- tax: Union[float, None] = None
- tags: Set[str] = set()
-
-
-@app.post("/items/", response_model=Item, tags=["items"])
-async def create_item(item: Item):
- return item
-
-
-@app.get("/items/", tags=["items"])
-async def read_items():
- return [{"name": "Foo", "price": 42}]
-
-
-@app.get("/users/", tags=["users"])
-async def read_users():
- return [{"username": "johndoe"}]
+++ /dev/null
-from typing import Set, Union
-
-from fastapi import FastAPI
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- tax: Union[float, None] = None
- tags: Set[str] = set()
-
-
-@app.post(
- "/items/",
- response_model=Item,
- summary="Create an item",
- description="Create an item with all the information, name, description, price, tax and a set of unique tags",
-)
-async def create_item(item: Item):
- return item
+++ /dev/null
-from typing import Set, Union
-
-from fastapi import FastAPI
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- tax: Union[float, None] = None
- tags: Set[str] = set()
-
-
-@app.post("/items/", response_model=Item, summary="Create an item")
-async def create_item(item: Item):
- """
- Create an item with all the information:
-
- - **name**: each item must have a name
- - **description**: a long description
- - **price**: required
- - **tax**: if the item doesn't have tax, you can omit this
- - **tags**: a set of unique tag strings for this item
- """
- return item
+++ /dev/null
-from typing import Set, Union
-
-from fastapi import FastAPI
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- tax: Union[float, None] = None
- tags: Set[str] = set()
-
-
-@app.post(
- "/items/",
- response_model=Item,
- summary="Create an item",
- response_description="The created item",
-)
-async def create_item(item: Item):
- """
- Create an item with all the information:
-
- - **name**: each item must have a name
- - **description**: a long description
- - **price**: required
- - **tax**: if the item doesn't have tax, you can omit this
- - **tags**: a set of unique tag strings for this item
- """
- return item
+++ /dev/null
-from typing import Union
-
-from fastapi import FastAPI, Path, Query
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-@app.get("/items/{item_id}")
-async def read_items(
- item_id: Annotated[int, Path(title="The ID of the item to get")],
- q: Annotated[Union[str, None], Query(alias="item-query")] = None,
-):
- results = {"item_id": item_id}
- if q:
- results.update({"q": q})
- return results
+++ /dev/null
-from fastapi import FastAPI, Path
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-@app.get("/items/{item_id}")
-async def read_items(
- q: str, item_id: Annotated[int, Path(title="The ID of the item to get")]
-):
- results = {"item_id": item_id}
- if q:
- results.update({"q": q})
- return results
+++ /dev/null
-from fastapi import FastAPI, Path
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-@app.get("/items/{item_id}")
-async def read_items(
- item_id: Annotated[int, Path(title="The ID of the item to get")], q: str
-):
- results = {"item_id": item_id}
- if q:
- results.update({"q": q})
- return results
+++ /dev/null
-from fastapi import FastAPI, Path
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-@app.get("/items/{item_id}")
-async def read_items(
- item_id: Annotated[int, Path(title="The ID of the item to get", ge=1)], q: str
-):
- results = {"item_id": item_id}
- if q:
- results.update({"q": q})
- return results
+++ /dev/null
-from fastapi import FastAPI, Path
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-@app.get("/items/{item_id}")
-async def read_items(
- item_id: Annotated[int, Path(title="The ID of the item to get", gt=0, le=1000)],
- q: str,
-):
- results = {"item_id": item_id}
- if q:
- results.update({"q": q})
- return results
+++ /dev/null
-from fastapi import FastAPI, Path, Query
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-@app.get("/items/{item_id}")
-async def read_items(
- *,
- item_id: Annotated[int, Path(title="The ID of the item to get", ge=0, le=1000)],
- q: str,
- size: Annotated[float, Query(gt=0, lt=10.5)],
-):
- results = {"item_id": item_id}
- if q:
- results.update({"q": q})
- if size:
- results.update({"size": size})
- return results
+++ /dev/null
-from typing import Union
-
-from fastapi import FastAPI
-from fastapi.temp_pydantic_v1_params import Body
-from pydantic.v1 import BaseModel
-from typing_extensions import Annotated
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- size: float
-
-
-app = FastAPI()
-
-
-@app.post("/items/")
-async def create_item(item: Annotated[Item, Body(embed=True)]) -> Item:
- return item
+++ /dev/null
-from typing import List
-
-
-def process_items(items: List[str]):
- for item in items:
- print(item)
+++ /dev/null
-from typing import Set, Tuple
-
-
-def process_items(items_t: Tuple[int, int, str], items_s: Set[bytes]):
- return items_t, items_s
+++ /dev/null
-from typing import Dict
-
-
-def process_items(prices: Dict[str, float]):
- for item_name, item_price in prices.items():
- print(item_name)
- print(item_price)
+++ /dev/null
-from datetime import datetime
-from typing import List, Union
-
-from pydantic import BaseModel
-
-
-class User(BaseModel):
- id: int
- name: str = "John Doe"
- signup_ts: Union[datetime, None] = None
- friends: List[int] = []
-
-
-external_data = {
- "id": "123",
- "signup_ts": "2017-06-01 12:22",
- "friends": [1, "2", b"3"],
-}
-user = User(**external_data)
-print(user)
-# > User id=123 name='John Doe' signup_ts=datetime.datetime(2017, 6, 1, 12, 22) friends=[1, 2, 3]
-print(user.id)
-# > 123
+++ /dev/null
-from typing_extensions import Annotated
-
-
-def say_hello(name: Annotated[str, "this is just metadata"]) -> str:
- return f"Hello {name}"
+++ /dev/null
-from typing import List
-
-from fastapi import FastAPI, Query
-from pydantic import BaseModel, Field
-from typing_extensions import Literal
-
-app = FastAPI()
-
-
-class FilterParams(BaseModel):
- limit: int = Field(100, gt=0, le=100)
- offset: int = Field(0, ge=0)
- order_by: Literal["created_at", "updated_at"] = "created_at"
- tags: List[str] = []
-
-
-@app.get("/items/")
-async def read_items(filter_query: FilterParams = Query()):
- return filter_query
+++ /dev/null
-from typing import List
-
-from fastapi import FastAPI, Query
-from pydantic import BaseModel, Field
-from typing_extensions import Annotated, Literal
-
-app = FastAPI()
-
-
-class FilterParams(BaseModel):
- limit: int = Field(100, gt=0, le=100)
- offset: int = Field(0, ge=0)
- order_by: Literal["created_at", "updated_at"] = "created_at"
- tags: List[str] = []
-
-
-@app.get("/items/")
-async def read_items(filter_query: Annotated[FilterParams, Query()]):
- return filter_query
+from typing import Annotated, Literal
+
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
-from typing_extensions import Annotated, Literal
app = FastAPI()
+from typing import Literal
+
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
-from typing_extensions import Literal
app = FastAPI()
+++ /dev/null
-from typing import List
-
-from fastapi import FastAPI, Query
-from pydantic import BaseModel, Field
-from typing_extensions import Literal
-
-app = FastAPI()
-
-
-class FilterParams(BaseModel):
- model_config = {"extra": "forbid"}
-
- limit: int = Field(100, gt=0, le=100)
- offset: int = Field(0, ge=0)
- order_by: Literal["created_at", "updated_at"] = "created_at"
- tags: List[str] = []
-
-
-@app.get("/items/")
-async def read_items(filter_query: FilterParams = Query()):
- return filter_query
+++ /dev/null
-from typing import List
-
-from fastapi import FastAPI, Query
-from pydantic import BaseModel, Field
-from typing_extensions import Annotated, Literal
-
-app = FastAPI()
-
-
-class FilterParams(BaseModel):
- model_config = {"extra": "forbid"}
-
- limit: int = Field(100, gt=0, le=100)
- offset: int = Field(0, ge=0)
- order_by: Literal["created_at", "updated_at"] = "created_at"
- tags: List[str] = []
-
-
-@app.get("/items/")
-async def read_items(filter_query: Annotated[FilterParams, Query()]):
- return filter_query
+from typing import Annotated, Literal
+
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
-from typing_extensions import Annotated, Literal
app = FastAPI()
+++ /dev/null
-from typing import List
-
-from fastapi import FastAPI, Query
-from pydantic import BaseModel, Field
-from typing_extensions import Literal
-
-app = FastAPI()
-
-
-class FilterParams(BaseModel):
- class Config:
- extra = "forbid"
-
- limit: int = Field(100, gt=0, le=100)
- offset: int = Field(0, ge=0)
- order_by: Literal["created_at", "updated_at"] = "created_at"
- tags: List[str] = []
-
-
-@app.get("/items/")
-async def read_items(filter_query: FilterParams = Query()):
- return filter_query
+++ /dev/null
-from typing import List
-
-from fastapi import FastAPI, Query
-from pydantic import BaseModel, Field
-from typing_extensions import Annotated, Literal
-
-app = FastAPI()
-
-
-class FilterParams(BaseModel):
- class Config:
- extra = "forbid"
-
- limit: int = Field(100, gt=0, le=100)
- offset: int = Field(0, ge=0)
- order_by: Literal["created_at", "updated_at"] = "created_at"
- tags: List[str] = []
-
-
-@app.get("/items/")
-async def read_items(filter_query: Annotated[FilterParams, Query()]):
- return filter_query
+from typing import Annotated, Literal
+
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
-from typing_extensions import Annotated, Literal
app = FastAPI()
+from typing import Literal
+
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
-from typing_extensions import Literal
app = FastAPI()
+from typing import Literal
+
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
-from typing_extensions import Literal
app = FastAPI()
+++ /dev/null
-from typing import Union
-
-from fastapi import FastAPI
-
-app = FastAPI()
-
-
-@app.get("/items/{item_id}")
-async def read_user_item(
- item_id: str, needy: str, skip: int = 0, limit: Union[int, None] = None
-):
- item = {"item_id": item_id, "needy": needy, "skip": skip, "limit": limit}
- return item
-from typing import Union
+from typing import Annotated, Union
from fastapi import FastAPI, Query
-from typing_extensions import Annotated
app = FastAPI()
+++ /dev/null
-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
+++ /dev/null
-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
+++ /dev/null
-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
+++ /dev/null
-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
+++ /dev/null
-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
+++ /dev/null
-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
+++ /dev/null
-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
+++ /dev/null
-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
+++ /dev/null
-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
+++ /dev/null
-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
+++ /dev/null
-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
+++ /dev/null
-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
+++ /dev/null
-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
+++ /dev/null
-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
+++ /dev/null
-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"}
+++ /dev/null
-import random
-from typing import Union
-
-from fastapi import FastAPI
-from pydantic import AfterValidator
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-data = {
- "isbn-9781529046137": "The Hitchhiker's Guide to the Galaxy",
- "imdb-tt0371724": "The Hitchhiker's Guide to the Galaxy",
- "isbn-9781439512982": "Isaac Asimov: The Complete Stories, Vol. 2",
-}
-
-
-def check_valid_id(id: str):
- if not id.startswith(("isbn-", "imdb-")):
- raise ValueError('Invalid ID format, it must start with "isbn-" or "imdb-"')
- return id
-
-
-@app.get("/items/")
-async def read_items(
- id: Annotated[Union[str, None], AfterValidator(check_valid_id)] = None,
-):
- if id:
- item = data.get(id)
- else:
- id, item = random.choice(list(data.items()))
- return {"id": id, "name": item}
+++ /dev/null
-from typing import Union
-
-from fastapi import FastAPI, File, UploadFile
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-@app.post("/files/")
-async def create_file(file: Annotated[Union[bytes, None], File()] = None):
- if not file:
- return {"message": "No file sent"}
- else:
- return {"file_size": len(file)}
-
-
-@app.post("/uploadfile/")
-async def create_upload_file(file: Union[UploadFile, None] = None):
- if not file:
- return {"message": "No upload file sent"}
- else:
- return {"filename": file.filename}
+++ /dev/null
-from fastapi import FastAPI, File, UploadFile
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-@app.post("/files/")
-async def create_file(file: Annotated[bytes, File(description="A file read as bytes")]):
- return {"file_size": len(file)}
-
-
-@app.post("/uploadfile/")
-async def create_upload_file(
- file: Annotated[UploadFile, File(description="A file read as UploadFile")],
-):
- return {"filename": file.filename}
+++ /dev/null
-from fastapi import FastAPI, File, UploadFile
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-@app.post("/files/")
-async def create_file(file: Annotated[bytes, File()]):
- return {"file_size": len(file)}
-
-
-@app.post("/uploadfile/")
-async def create_upload_file(file: UploadFile):
- return {"filename": file.filename}
+++ /dev/null
-from typing import List
-
-from fastapi import FastAPI, File, UploadFile
-from fastapi.responses import HTMLResponse
-
-app = FastAPI()
-
-
-@app.post("/files/")
-async def create_files(files: List[bytes] = File()):
- return {"file_sizes": [len(file) for file in files]}
-
-
-@app.post("/uploadfiles/")
-async def create_upload_files(files: List[UploadFile]):
- return {"filenames": [file.filename for file in files]}
-
-
-@app.get("/")
-async def main():
- content = """
-<body>
-<form action="/files/" enctype="multipart/form-data" method="post">
-<input name="files" type="file" multiple>
-<input type="submit">
-</form>
-<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
-<input name="files" type="file" multiple>
-<input type="submit">
-</form>
-</body>
- """
- return HTMLResponse(content=content)
+++ /dev/null
-from typing import List
-
-from fastapi import FastAPI, File, UploadFile
-from fastapi.responses import HTMLResponse
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-@app.post("/files/")
-async def create_files(files: Annotated[List[bytes], File()]):
- return {"file_sizes": [len(file) for file in files]}
-
-
-@app.post("/uploadfiles/")
-async def create_upload_files(files: List[UploadFile]):
- return {"filenames": [file.filename for file in files]}
-
-
-@app.get("/")
-async def main():
- content = """
-<body>
-<form action="/files/" enctype="multipart/form-data" method="post">
-<input name="files" type="file" multiple>
-<input type="submit">
-</form>
-<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
-<input name="files" type="file" multiple>
-<input type="submit">
-</form>
-</body>
- """
- return HTMLResponse(content=content)
+++ /dev/null
-from typing import List
-
-from fastapi import FastAPI, File, UploadFile
-from fastapi.responses import HTMLResponse
-
-app = FastAPI()
-
-
-@app.post("/files/")
-async def create_files(
- files: List[bytes] = File(description="Multiple files as bytes"),
-):
- return {"file_sizes": [len(file) for file in files]}
-
-
-@app.post("/uploadfiles/")
-async def create_upload_files(
- files: List[UploadFile] = File(description="Multiple files as UploadFile"),
-):
- return {"filenames": [file.filename for file in files]}
-
-
-@app.get("/")
-async def main():
- content = """
-<body>
-<form action="/files/" enctype="multipart/form-data" method="post">
-<input name="files" type="file" multiple>
-<input type="submit">
-</form>
-<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
-<input name="files" type="file" multiple>
-<input type="submit">
-</form>
-</body>
- """
- return HTMLResponse(content=content)
+++ /dev/null
-from typing import List
-
-from fastapi import FastAPI, File, UploadFile
-from fastapi.responses import HTMLResponse
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-@app.post("/files/")
-async def create_files(
- files: Annotated[List[bytes], File(description="Multiple files as bytes")],
-):
- return {"file_sizes": [len(file) for file in files]}
-
-
-@app.post("/uploadfiles/")
-async def create_upload_files(
- files: Annotated[
- List[UploadFile], File(description="Multiple files as UploadFile")
- ],
-):
- return {"filenames": [file.filename for file in files]}
-
-
-@app.get("/")
-async def main():
- content = """
-<body>
-<form action="/files/" enctype="multipart/form-data" method="post">
-<input name="files" type="file" multiple>
-<input type="submit">
-</form>
-<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
-<input name="files" type="file" multiple>
-<input type="submit">
-</form>
-</body>
- """
- return HTMLResponse(content=content)
+++ /dev/null
-from fastapi import FastAPI, Form
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class FormData(BaseModel):
- username: str
- password: str
-
-
-@app.post("/login/")
-async def login(data: Annotated[FormData, Form()]):
- return data
+++ /dev/null
-from fastapi import FastAPI, Form
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class FormData(BaseModel):
- username: str
- password: str
- model_config = {"extra": "forbid"}
-
-
-@app.post("/login/")
-async def login(data: Annotated[FormData, Form()]):
- return data
+++ /dev/null
-from fastapi import FastAPI, Form
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class FormData(BaseModel):
- username: str
- password: str
-
- class Config:
- extra = "forbid"
-
-
-@app.post("/login/")
-async def login(data: Annotated[FormData, Form()]):
- return data
+++ /dev/null
-from fastapi import FastAPI, Form
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-@app.post("/login/")
-async def login(username: Annotated[str, Form()], password: Annotated[str, Form()]):
- return {"username": username}
+++ /dev/null
-from fastapi import FastAPI, File, Form, UploadFile
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-@app.post("/files/")
-async def create_file(
- file: Annotated[bytes, File()],
- fileb: Annotated[UploadFile, File()],
- token: Annotated[str, Form()],
-):
- return {
- "file_size": len(file),
- "token": token,
- "fileb_content_type": fileb.content_type,
- }
+++ /dev/null
-from typing import Any, List, Union
-
-from fastapi import FastAPI
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- tax: Union[float, None] = None
- tags: List[str] = []
-
-
-@app.post("/items/", response_model=Item)
-async def create_item(item: Item) -> Any:
- return item
-
-
-@app.get("/items/", response_model=List[Item])
-async def read_items() -> Any:
- return [
- {"name": "Portal Gun", "price": 42.0},
- {"name": "Plumbus", "price": 32.0},
- ]
+++ /dev/null
-from typing import List, Union
-
-from fastapi import FastAPI
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- tax: Union[float, None] = None
- tags: List[str] = []
-
-
-@app.post("/items/")
-async def create_item(item: Item) -> Item:
- return item
-
-
-@app.get("/items/")
-async def read_items() -> List[Item]:
- return [
- Item(name="Portal Gun", price=42.0),
- Item(name="Plumbus", price=32.0),
- ]
+++ /dev/null
-from typing import List, Union
-
-from fastapi import FastAPI
-from pydantic import BaseModel
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- tax: float = 10.5
- tags: List[str] = []
-
-
-items = {
- "foo": {"name": "Foo", "price": 50.2},
- "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
- "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
-}
-
-
-@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
-async def read_item(item_id: str):
- return items[item_id]
+++ /dev/null
-from typing import Union
-
-from fastapi import Body, FastAPI
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- tax: Union[float, None] = None
-
-
-@app.put("/items/{item_id}")
-async def update_item(
- item_id: int,
- item: Annotated[
- Item,
- Body(
- examples=[
- {
- "name": "Foo",
- "description": "A very nice Item",
- "price": 35.4,
- "tax": 3.2,
- }
- ],
- ),
- ],
-):
- results = {"item_id": item_id, "item": item}
- return results
+++ /dev/null
-from typing import Union
-
-from fastapi import Body, FastAPI
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- tax: Union[float, None] = None
-
-
-@app.put("/items/{item_id}")
-async def update_item(
- *,
- item_id: int,
- item: Annotated[
- Item,
- Body(
- examples=[
- {
- "name": "Foo",
- "description": "A very nice Item",
- "price": 35.4,
- "tax": 3.2,
- },
- {
- "name": "Bar",
- "price": "35.4",
- },
- {
- "name": "Baz",
- "price": "thirty five point four",
- },
- ],
- ),
- ],
-):
- results = {"item_id": item_id, "item": item}
- return results
+++ /dev/null
-from typing import Union
-
-from fastapi import Body, FastAPI
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
- price: float
- tax: Union[float, None] = None
-
-
-@app.put("/items/{item_id}")
-async def update_item(
- *,
- item_id: int,
- item: Annotated[
- Item,
- Body(
- openapi_examples={
- "normal": {
- "summary": "A normal example",
- "description": "A **normal** item works correctly.",
- "value": {
- "name": "Foo",
- "description": "A very nice Item",
- "price": 35.4,
- "tax": 3.2,
- },
- },
- "converted": {
- "summary": "An example with converted data",
- "description": "FastAPI can convert price `strings` to actual `numbers` automatically",
- "value": {
- "name": "Bar",
- "price": "35.4",
- },
- },
- "invalid": {
- "summary": "Invalid data is rejected with an error",
- "value": {
- "name": "Baz",
- "price": "thirty five point four",
- },
- },
- },
- ),
- ],
-):
- results = {"item_id": item_id, "item": item}
- return results
+++ /dev/null
-from fastapi import Depends, FastAPI
-from fastapi.security import OAuth2PasswordBearer
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
-
-
-@app.get("/items/")
-async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
- return {"token": token}
+++ /dev/null
-from typing import Union
-
-from fastapi import Depends, FastAPI
-from fastapi.security import OAuth2PasswordBearer
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
-
-
-class User(BaseModel):
- username: str
- email: Union[str, None] = None
- full_name: Union[str, None] = None
- disabled: Union[bool, None] = None
-
-
-def fake_decode_token(token):
- return User(
- username=token + "fakedecoded", email="john@example.com", full_name="John Doe"
- )
-
-
-async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):
- user = fake_decode_token(token)
- return user
-
-
-@app.get("/users/me")
-async def read_users_me(current_user: Annotated[User, Depends(get_current_user)]):
- return current_user
+++ /dev/null
-from typing import Union
-
-from fastapi import Depends, FastAPI, HTTPException, status
-from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-fake_users_db = {
- "johndoe": {
- "username": "johndoe",
- "full_name": "John Doe",
- "email": "johndoe@example.com",
- "hashed_password": "fakehashedsecret",
- "disabled": False,
- },
- "alice": {
- "username": "alice",
- "full_name": "Alice Wonderson",
- "email": "alice@example.com",
- "hashed_password": "fakehashedsecret2",
- "disabled": True,
- },
-}
-
-app = FastAPI()
-
-
-def fake_hash_password(password: str):
- return "fakehashed" + password
-
-
-oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
-
-
-class User(BaseModel):
- username: str
- email: Union[str, None] = None
- full_name: Union[str, None] = None
- disabled: Union[bool, None] = None
-
-
-class UserInDB(User):
- hashed_password: str
-
-
-def get_user(db, username: str):
- if username in db:
- user_dict = db[username]
- return UserInDB(**user_dict)
-
-
-def fake_decode_token(token):
- # This doesn't provide any security at all
- # Check the next version
- user = get_user(fake_users_db, token)
- return user
-
-
-async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):
- user = fake_decode_token(token)
- if not user:
- raise HTTPException(
- status_code=status.HTTP_401_UNAUTHORIZED,
- detail="Not authenticated",
- headers={"WWW-Authenticate": "Bearer"},
- )
- return user
-
-
-async def get_current_active_user(
- current_user: Annotated[User, Depends(get_current_user)],
-):
- if current_user.disabled:
- raise HTTPException(status_code=400, detail="Inactive user")
- return current_user
-
-
-@app.post("/token")
-async def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]):
- user_dict = fake_users_db.get(form_data.username)
- if not user_dict:
- raise HTTPException(status_code=400, detail="Incorrect username or password")
- user = UserInDB(**user_dict)
- hashed_password = fake_hash_password(form_data.password)
- if not hashed_password == user.hashed_password:
- raise HTTPException(status_code=400, detail="Incorrect username or password")
-
- return {"access_token": user.username, "token_type": "bearer"}
-
-
-@app.get("/users/me")
-async def read_users_me(
- current_user: Annotated[User, Depends(get_current_active_user)],
-):
- return current_user
+++ /dev/null
-from datetime import datetime, timedelta, timezone
-from typing import Union
-
-import jwt
-from fastapi import Depends, FastAPI, HTTPException, status
-from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
-from jwt.exceptions import InvalidTokenError
-from pwdlib import PasswordHash
-from pydantic import BaseModel
-from typing_extensions import Annotated
-
-# to get a string like this run:
-# openssl rand -hex 32
-SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
-ALGORITHM = "HS256"
-ACCESS_TOKEN_EXPIRE_MINUTES = 30
-
-
-fake_users_db = {
- "johndoe": {
- "username": "johndoe",
- "full_name": "John Doe",
- "email": "johndoe@example.com",
- "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc",
- "disabled": False,
- }
-}
-
-
-class Token(BaseModel):
- access_token: str
- token_type: str
-
-
-class TokenData(BaseModel):
- username: Union[str, None] = None
-
-
-class User(BaseModel):
- username: str
- email: Union[str, None] = None
- full_name: Union[str, None] = None
- disabled: Union[bool, None] = None
-
-
-class UserInDB(User):
- hashed_password: str
-
-
-password_hash = PasswordHash.recommended()
-
-oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
-
-app = FastAPI()
-
-
-def verify_password(plain_password, hashed_password):
- return password_hash.verify(plain_password, hashed_password)
-
-
-def get_password_hash(password):
- return password_hash.hash(password)
-
-
-def get_user(db, username: str):
- if username in db:
- user_dict = db[username]
- return UserInDB(**user_dict)
-
-
-def authenticate_user(fake_db, username: str, password: str):
- user = get_user(fake_db, username)
- if not user:
- return False
- if not verify_password(password, user.hashed_password):
- return False
- return user
-
-
-def create_access_token(data: dict, expires_delta: Union[timedelta, None] = None):
- to_encode = data.copy()
- if expires_delta:
- expire = datetime.now(timezone.utc) + expires_delta
- else:
- expire = datetime.now(timezone.utc) + timedelta(minutes=15)
- to_encode.update({"exp": expire})
- encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
- return encoded_jwt
-
-
-async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):
- credentials_exception = HTTPException(
- status_code=status.HTTP_401_UNAUTHORIZED,
- detail="Could not validate credentials",
- headers={"WWW-Authenticate": "Bearer"},
- )
- try:
- payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
- username = payload.get("sub")
- if username is None:
- raise credentials_exception
- token_data = TokenData(username=username)
- except InvalidTokenError:
- raise credentials_exception
- user = get_user(fake_users_db, username=token_data.username)
- if user is None:
- raise credentials_exception
- return user
-
-
-async def get_current_active_user(
- current_user: Annotated[User, Depends(get_current_user)],
-):
- if current_user.disabled:
- raise HTTPException(status_code=400, detail="Inactive user")
- return current_user
-
-
-@app.post("/token")
-async def login_for_access_token(
- form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
-) -> Token:
- user = authenticate_user(fake_users_db, form_data.username, form_data.password)
- if not user:
- raise HTTPException(
- status_code=status.HTTP_401_UNAUTHORIZED,
- detail="Incorrect username or password",
- headers={"WWW-Authenticate": "Bearer"},
- )
- access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
- access_token = create_access_token(
- data={"sub": user.username}, expires_delta=access_token_expires
- )
- return Token(access_token=access_token, token_type="bearer")
-
-
-@app.get("/users/me/", response_model=User)
-async def read_users_me(
- current_user: Annotated[User, Depends(get_current_active_user)],
-):
- return current_user
-
-
-@app.get("/users/me/items/")
-async def read_own_items(
- current_user: Annotated[User, Depends(get_current_active_user)],
-):
- return [{"item_id": "Foo", "owner": current_user.username}]
+++ /dev/null
-from datetime import datetime, timedelta, timezone
-from typing import List, Union
-
-import jwt
-from fastapi import Depends, FastAPI, HTTPException, Security, status
-from fastapi.security import (
- OAuth2PasswordBearer,
- OAuth2PasswordRequestForm,
- SecurityScopes,
-)
-from jwt.exceptions import InvalidTokenError
-from pwdlib import PasswordHash
-from pydantic import BaseModel, ValidationError
-
-# to get a string like this run:
-# openssl rand -hex 32
-SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
-ALGORITHM = "HS256"
-ACCESS_TOKEN_EXPIRE_MINUTES = 30
-
-
-fake_users_db = {
- "johndoe": {
- "username": "johndoe",
- "full_name": "John Doe",
- "email": "johndoe@example.com",
- "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc",
- "disabled": False,
- },
- "alice": {
- "username": "alice",
- "full_name": "Alice Chains",
- "email": "alicechains@example.com",
- "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$g2/AV1zwopqUntPKJavBFw$BwpRGDCyUHLvHICnwijyX8ROGoiUPwNKZ7915MeYfCE",
- "disabled": True,
- },
-}
-
-
-class Token(BaseModel):
- access_token: str
- token_type: str
-
-
-class TokenData(BaseModel):
- username: Union[str, None] = None
- scopes: List[str] = []
-
-
-class User(BaseModel):
- username: str
- email: Union[str, None] = None
- full_name: Union[str, None] = None
- disabled: Union[bool, None] = None
-
-
-class UserInDB(User):
- hashed_password: str
-
-
-password_hash = PasswordHash.recommended()
-
-oauth2_scheme = OAuth2PasswordBearer(
- tokenUrl="token",
- scopes={"me": "Read information about the current user.", "items": "Read items."},
-)
-
-app = FastAPI()
-
-
-def verify_password(plain_password, hashed_password):
- return password_hash.verify(plain_password, hashed_password)
-
-
-def get_password_hash(password):
- return password_hash.hash(password)
-
-
-def get_user(db, username: str):
- if username in db:
- user_dict = db[username]
- return UserInDB(**user_dict)
-
-
-def authenticate_user(fake_db, username: str, password: str):
- user = get_user(fake_db, username)
- if not user:
- return False
- if not verify_password(password, user.hashed_password):
- return False
- return user
-
-
-def create_access_token(data: dict, expires_delta: Union[timedelta, None] = None):
- to_encode = data.copy()
- if expires_delta:
- expire = datetime.now(timezone.utc) + expires_delta
- else:
- expire = datetime.now(timezone.utc) + timedelta(minutes=15)
- to_encode.update({"exp": expire})
- encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
- return encoded_jwt
-
-
-async def get_current_user(
- security_scopes: SecurityScopes, token: str = Depends(oauth2_scheme)
-):
- if security_scopes.scopes:
- authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
- else:
- authenticate_value = "Bearer"
- credentials_exception = HTTPException(
- status_code=status.HTTP_401_UNAUTHORIZED,
- detail="Could not validate credentials",
- headers={"WWW-Authenticate": authenticate_value},
- )
- try:
- payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
- username: str = payload.get("sub")
- if username is None:
- raise credentials_exception
- scope: str = payload.get("scope", "")
- token_scopes = scope.split(" ")
- token_data = TokenData(scopes=token_scopes, username=username)
- except (InvalidTokenError, ValidationError):
- raise credentials_exception
- user = get_user(fake_users_db, username=token_data.username)
- if user is None:
- raise credentials_exception
- for scope in security_scopes.scopes:
- if scope not in token_data.scopes:
- raise HTTPException(
- status_code=status.HTTP_401_UNAUTHORIZED,
- detail="Not enough permissions",
- headers={"WWW-Authenticate": authenticate_value},
- )
- return user
-
-
-async def get_current_active_user(
- current_user: User = Security(get_current_user, scopes=["me"]),
-):
- if current_user.disabled:
- raise HTTPException(status_code=400, detail="Inactive user")
- return current_user
-
-
-@app.post("/token")
-async def login_for_access_token(
- form_data: OAuth2PasswordRequestForm = Depends(),
-) -> Token:
- user = authenticate_user(fake_users_db, form_data.username, form_data.password)
- if not user:
- raise HTTPException(status_code=400, detail="Incorrect username or password")
- access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
- access_token = create_access_token(
- data={"sub": user.username, "scope": " ".join(form_data.scopes)},
- expires_delta=access_token_expires,
- )
- return Token(access_token=access_token, token_type="bearer")
-
-
-@app.get("/users/me/", response_model=User)
-async def read_users_me(current_user: User = Depends(get_current_active_user)):
- return current_user
-
-
-@app.get("/users/me/items/")
-async def read_own_items(
- current_user: User = Security(get_current_active_user, scopes=["items"]),
-):
- return [{"item_id": "Foo", "owner": current_user.username}]
-
-
-@app.get("/status/")
-async def read_system_status(current_user: User = Depends(get_current_user)):
- return {"status": "ok"}
+++ /dev/null
-from datetime import datetime, timedelta, timezone
-from typing import List, Union
-
-import jwt
-from fastapi import Depends, FastAPI, HTTPException, Security, status
-from fastapi.security import (
- OAuth2PasswordBearer,
- OAuth2PasswordRequestForm,
- SecurityScopes,
-)
-from jwt.exceptions import InvalidTokenError
-from pwdlib import PasswordHash
-from pydantic import BaseModel, ValidationError
-from typing_extensions import Annotated
-
-# to get a string like this run:
-# openssl rand -hex 32
-SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
-ALGORITHM = "HS256"
-ACCESS_TOKEN_EXPIRE_MINUTES = 30
-
-
-fake_users_db = {
- "johndoe": {
- "username": "johndoe",
- "full_name": "John Doe",
- "email": "johndoe@example.com",
- "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc",
- "disabled": False,
- },
- "alice": {
- "username": "alice",
- "full_name": "Alice Chains",
- "email": "alicechains@example.com",
- "hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$g2/AV1zwopqUntPKJavBFw$BwpRGDCyUHLvHICnwijyX8ROGoiUPwNKZ7915MeYfCE",
- "disabled": True,
- },
-}
-
-
-class Token(BaseModel):
- access_token: str
- token_type: str
-
-
-class TokenData(BaseModel):
- username: Union[str, None] = None
- scopes: List[str] = []
-
-
-class User(BaseModel):
- username: str
- email: Union[str, None] = None
- full_name: Union[str, None] = None
- disabled: Union[bool, None] = None
-
-
-class UserInDB(User):
- hashed_password: str
-
-
-password_hash = PasswordHash.recommended()
-
-oauth2_scheme = OAuth2PasswordBearer(
- tokenUrl="token",
- scopes={"me": "Read information about the current user.", "items": "Read items."},
-)
-
-app = FastAPI()
-
-
-def verify_password(plain_password, hashed_password):
- return password_hash.verify(plain_password, hashed_password)
-
-
-def get_password_hash(password):
- return password_hash.hash(password)
-
-
-def get_user(db, username: str):
- if username in db:
- user_dict = db[username]
- return UserInDB(**user_dict)
-
-
-def authenticate_user(fake_db, username: str, password: str):
- user = get_user(fake_db, username)
- if not user:
- return False
- if not verify_password(password, user.hashed_password):
- return False
- return user
-
-
-def create_access_token(data: dict, expires_delta: Union[timedelta, None] = None):
- to_encode = data.copy()
- if expires_delta:
- expire = datetime.now(timezone.utc) + expires_delta
- else:
- expire = datetime.now(timezone.utc) + timedelta(minutes=15)
- to_encode.update({"exp": expire})
- encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
- return encoded_jwt
-
-
-async def get_current_user(
- security_scopes: SecurityScopes, token: Annotated[str, Depends(oauth2_scheme)]
-):
- if security_scopes.scopes:
- authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
- else:
- authenticate_value = "Bearer"
- credentials_exception = HTTPException(
- status_code=status.HTTP_401_UNAUTHORIZED,
- detail="Could not validate credentials",
- headers={"WWW-Authenticate": authenticate_value},
- )
- try:
- payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
- username = payload.get("sub")
- if username is None:
- raise credentials_exception
- scope: str = payload.get("scope", "")
- token_scopes = scope.split(" ")
- token_data = TokenData(scopes=token_scopes, username=username)
- except (InvalidTokenError, ValidationError):
- raise credentials_exception
- user = get_user(fake_users_db, username=token_data.username)
- if user is None:
- raise credentials_exception
- for scope in security_scopes.scopes:
- if scope not in token_data.scopes:
- raise HTTPException(
- status_code=status.HTTP_401_UNAUTHORIZED,
- detail="Not enough permissions",
- headers={"WWW-Authenticate": authenticate_value},
- )
- return user
-
-
-async def get_current_active_user(
- current_user: Annotated[User, Security(get_current_user, scopes=["me"])],
-):
- if current_user.disabled:
- raise HTTPException(status_code=400, detail="Inactive user")
- return current_user
-
-
-@app.post("/token")
-async def login_for_access_token(
- form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
-) -> Token:
- user = authenticate_user(fake_users_db, form_data.username, form_data.password)
- if not user:
- raise HTTPException(status_code=400, detail="Incorrect username or password")
- access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
- access_token = create_access_token(
- data={"sub": user.username, "scope": " ".join(form_data.scopes)},
- expires_delta=access_token_expires,
- )
- return Token(access_token=access_token, token_type="bearer")
-
-
-@app.get("/users/me/", response_model=User)
-async def read_users_me(
- current_user: Annotated[User, Depends(get_current_active_user)],
-):
- return current_user
-
-
-@app.get("/users/me/items/")
-async def read_own_items(
- current_user: Annotated[User, Security(get_current_active_user, scopes=["items"])],
-):
- return [{"item_id": "Foo", "owner": current_user.username}]
-
-
-@app.get("/status/")
-async def read_system_status(current_user: Annotated[User, Depends(get_current_user)]):
- return {"status": "ok"}
+++ /dev/null
-from fastapi import Depends, FastAPI
-from fastapi.security import HTTPBasic, HTTPBasicCredentials
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-security = HTTPBasic()
-
-
-@app.get("/users/me")
-def read_current_user(credentials: Annotated[HTTPBasicCredentials, Depends(security)]):
- return {"username": credentials.username, "password": credentials.password}
+++ /dev/null
-import secrets
-
-from fastapi import Depends, FastAPI, HTTPException, status
-from fastapi.security import HTTPBasic, HTTPBasicCredentials
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-security = HTTPBasic()
-
-
-def get_current_username(
- credentials: Annotated[HTTPBasicCredentials, Depends(security)],
-):
- current_username_bytes = credentials.username.encode("utf8")
- correct_username_bytes = b"stanleyjobson"
- is_correct_username = secrets.compare_digest(
- current_username_bytes, correct_username_bytes
- )
- current_password_bytes = credentials.password.encode("utf8")
- correct_password_bytes = b"swordfish"
- is_correct_password = secrets.compare_digest(
- current_password_bytes, correct_password_bytes
- )
- if not (is_correct_username and is_correct_password):
- raise HTTPException(
- status_code=status.HTTP_401_UNAUTHORIZED,
- detail="Incorrect username or password",
- headers={"WWW-Authenticate": "Basic"},
- )
- return credentials.username
-
-
-@app.get("/users/me")
-def read_current_user(username: Annotated[str, Depends(get_current_username)]):
- return {"username": username}
+++ /dev/null
-from typing import List, Union
-
-from fastapi import FastAPI
-from pydantic import BaseModel
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
-
-
-app = FastAPI()
-
-
-@app.post("/items/")
-def create_item(item: Item):
- return item
-
-
-@app.get("/items/")
-def read_items() -> List[Item]:
- return [
- Item(
- name="Portal Gun",
- description="Device to travel through the multi-rick-verse",
- ),
- Item(name="Plumbus"),
- ]
+++ /dev/null
-from typing import List, Union
-
-from fastapi import FastAPI
-from pydantic import BaseModel
-
-
-class Item(BaseModel):
- name: str
- description: Union[str, None] = None
-
-
-app = FastAPI(separate_input_output_schemas=False)
-
-
-@app.post("/items/")
-def create_item(item: Item):
- return item
-
-
-@app.get("/items/")
-def read_items() -> List[Item]:
- return [
- Item(
- name="Portal Gun",
- description="Device to travel through the multi-rick-verse",
- ),
- Item(name="Plumbus"),
- ]
+++ /dev/null
-from pydantic_settings import BaseSettings
-
-
-class Settings(BaseSettings):
- app_name: str = "Awesome API"
- admin_email: str
- items_per_user: int = 50
+++ /dev/null
-from functools import lru_cache
-
-from fastapi import Depends, FastAPI
-from typing_extensions import Annotated
-
-from .config import Settings
-
-app = FastAPI()
-
-
-@lru_cache
-def get_settings():
- return Settings()
-
-
-@app.get("/info")
-async def info(settings: Annotated[Settings, Depends(get_settings)]):
- return {
- "app_name": settings.app_name,
- "admin_email": settings.admin_email,
- "items_per_user": settings.items_per_user,
- }
+++ /dev/null
-from fastapi.testclient import TestClient
-
-from .config import Settings
-from .main import app, get_settings
-
-client = TestClient(app)
-
-
-def get_settings_override():
- return Settings(admin_email="testing_admin@example.com")
-
-
-app.dependency_overrides[get_settings] = get_settings_override
-
-
-def test_app():
- response = client.get("/info")
- data = response.json()
- assert data == {
- "app_name": "Awesome API",
- "admin_email": "testing_admin@example.com",
- "items_per_user": 50,
- }
+++ /dev/null
-from pydantic_settings import BaseSettings, SettingsConfigDict
-
-
-class Settings(BaseSettings):
- app_name: str = "Awesome API"
- admin_email: str
- items_per_user: int = 50
-
- model_config = SettingsConfigDict(env_file=".env")
+++ /dev/null
-from pydantic import BaseSettings
-
-
-class Settings(BaseSettings):
- app_name: str = "Awesome API"
- admin_email: str
- items_per_user: int = 50
-
- class Config:
- env_file = ".env"
+++ /dev/null
-from functools import lru_cache
-
-from fastapi import Depends, FastAPI
-from typing_extensions import Annotated
-
-from . import config
-
-app = FastAPI()
-
-
-@lru_cache
-def get_settings():
- return config.Settings()
-
-
-@app.get("/info")
-async def info(settings: Annotated[config.Settings, Depends(get_settings)]):
- return {
- "app_name": settings.app_name,
- "admin_email": settings.admin_email,
- "items_per_user": settings.items_per_user,
- }
+++ /dev/null
-from typing import List, Union
-
-from fastapi import Depends, FastAPI, HTTPException, Query
-from sqlmodel import Field, Session, SQLModel, create_engine, select
-
-
-class Hero(SQLModel, table=True):
- id: Union[int, None] = Field(default=None, primary_key=True)
- name: str = Field(index=True)
- age: Union[int, None] = Field(default=None, index=True)
- secret_name: str
-
-
-sqlite_file_name = "database.db"
-sqlite_url = f"sqlite:///{sqlite_file_name}"
-
-connect_args = {"check_same_thread": False}
-engine = create_engine(sqlite_url, connect_args=connect_args)
-
-
-def create_db_and_tables():
- SQLModel.metadata.create_all(engine)
-
-
-def get_session():
- with Session(engine) as session:
- yield session
-
-
-app = FastAPI()
-
-
-@app.on_event("startup")
-def on_startup():
- create_db_and_tables()
-
-
-@app.post("/heroes/")
-def create_hero(hero: Hero, session: Session = Depends(get_session)) -> Hero:
- session.add(hero)
- session.commit()
- session.refresh(hero)
- return hero
-
-
-@app.get("/heroes/")
-def read_heroes(
- session: Session = Depends(get_session),
- offset: int = 0,
- limit: int = Query(default=100, le=100),
-) -> List[Hero]:
- heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
- return heroes
-
-
-@app.get("/heroes/{hero_id}")
-def read_hero(hero_id: int, session: Session = Depends(get_session)) -> Hero:
- hero = session.get(Hero, hero_id)
- if not hero:
- raise HTTPException(status_code=404, detail="Hero not found")
- return hero
-
-
-@app.delete("/heroes/{hero_id}")
-def delete_hero(hero_id: int, session: Session = Depends(get_session)):
- hero = session.get(Hero, hero_id)
- if not hero:
- raise HTTPException(status_code=404, detail="Hero not found")
- session.delete(hero)
- session.commit()
- return {"ok": True}
+++ /dev/null
-from typing import List, Union
-
-from fastapi import Depends, FastAPI, HTTPException, Query
-from sqlmodel import Field, Session, SQLModel, create_engine, select
-from typing_extensions import Annotated
-
-
-class Hero(SQLModel, table=True):
- id: Union[int, None] = Field(default=None, primary_key=True)
- name: str = Field(index=True)
- age: Union[int, None] = Field(default=None, index=True)
- secret_name: str
-
-
-sqlite_file_name = "database.db"
-sqlite_url = f"sqlite:///{sqlite_file_name}"
-
-connect_args = {"check_same_thread": False}
-engine = create_engine(sqlite_url, connect_args=connect_args)
-
-
-def create_db_and_tables():
- SQLModel.metadata.create_all(engine)
-
-
-def get_session():
- with Session(engine) as session:
- yield session
-
-
-SessionDep = Annotated[Session, Depends(get_session)]
-
-app = FastAPI()
-
-
-@app.on_event("startup")
-def on_startup():
- create_db_and_tables()
-
-
-@app.post("/heroes/")
-def create_hero(hero: Hero, session: SessionDep) -> Hero:
- session.add(hero)
- session.commit()
- session.refresh(hero)
- return hero
-
-
-@app.get("/heroes/")
-def read_heroes(
- session: SessionDep,
- offset: int = 0,
- limit: Annotated[int, Query(le=100)] = 100,
-) -> List[Hero]:
- heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
- return heroes
-
-
-@app.get("/heroes/{hero_id}")
-def read_hero(hero_id: int, session: SessionDep) -> Hero:
- hero = session.get(Hero, hero_id)
- if not hero:
- raise HTTPException(status_code=404, detail="Hero not found")
- return hero
-
-
-@app.delete("/heroes/{hero_id}")
-def delete_hero(hero_id: int, session: SessionDep):
- hero = session.get(Hero, hero_id)
- if not hero:
- raise HTTPException(status_code=404, detail="Hero not found")
- session.delete(hero)
- session.commit()
- return {"ok": True}
+++ /dev/null
-from typing import List, Union
-
-from fastapi import Depends, FastAPI, HTTPException, Query
-from sqlmodel import Field, Session, SQLModel, create_engine, select
-
-
-class HeroBase(SQLModel):
- name: str = Field(index=True)
- age: Union[int, None] = Field(default=None, index=True)
-
-
-class Hero(HeroBase, table=True):
- id: Union[int, None] = Field(default=None, primary_key=True)
- secret_name: str
-
-
-class HeroPublic(HeroBase):
- id: int
-
-
-class HeroCreate(HeroBase):
- secret_name: str
-
-
-class HeroUpdate(HeroBase):
- name: Union[str, None] = None
- age: Union[int, None] = None
- secret_name: Union[str, None] = None
-
-
-sqlite_file_name = "database.db"
-sqlite_url = f"sqlite:///{sqlite_file_name}"
-
-connect_args = {"check_same_thread": False}
-engine = create_engine(sqlite_url, connect_args=connect_args)
-
-
-def create_db_and_tables():
- SQLModel.metadata.create_all(engine)
-
-
-def get_session():
- with Session(engine) as session:
- yield session
-
-
-app = FastAPI()
-
-
-@app.on_event("startup")
-def on_startup():
- create_db_and_tables()
-
-
-@app.post("/heroes/", response_model=HeroPublic)
-def create_hero(hero: HeroCreate, session: Session = Depends(get_session)):
- db_hero = Hero.model_validate(hero)
- session.add(db_hero)
- session.commit()
- session.refresh(db_hero)
- return db_hero
-
-
-@app.get("/heroes/", response_model=List[HeroPublic])
-def read_heroes(
- session: Session = Depends(get_session),
- offset: int = 0,
- limit: int = Query(default=100, le=100),
-):
- heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
- return heroes
-
-
-@app.get("/heroes/{hero_id}", response_model=HeroPublic)
-def read_hero(hero_id: int, session: Session = Depends(get_session)):
- hero = session.get(Hero, hero_id)
- if not hero:
- raise HTTPException(status_code=404, detail="Hero not found")
- return hero
-
-
-@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
-def update_hero(
- hero_id: int, hero: HeroUpdate, session: Session = Depends(get_session)
-):
- hero_db = session.get(Hero, hero_id)
- if not hero_db:
- raise HTTPException(status_code=404, detail="Hero not found")
- hero_data = hero.model_dump(exclude_unset=True)
- hero_db.sqlmodel_update(hero_data)
- session.add(hero_db)
- session.commit()
- session.refresh(hero_db)
- return hero_db
-
-
-@app.delete("/heroes/{hero_id}")
-def delete_hero(hero_id: int, session: Session = Depends(get_session)):
- hero = session.get(Hero, hero_id)
- if not hero:
- raise HTTPException(status_code=404, detail="Hero not found")
- session.delete(hero)
- session.commit()
- return {"ok": True}
+++ /dev/null
-from typing import List, Union
-
-from fastapi import Depends, FastAPI, HTTPException, Query
-from sqlmodel import Field, Session, SQLModel, create_engine, select
-from typing_extensions import Annotated
-
-
-class HeroBase(SQLModel):
- name: str = Field(index=True)
- age: Union[int, None] = Field(default=None, index=True)
-
-
-class Hero(HeroBase, table=True):
- id: Union[int, None] = Field(default=None, primary_key=True)
- secret_name: str
-
-
-class HeroPublic(HeroBase):
- id: int
-
-
-class HeroCreate(HeroBase):
- secret_name: str
-
-
-class HeroUpdate(HeroBase):
- name: Union[str, None] = None
- age: Union[int, None] = None
- secret_name: Union[str, None] = None
-
-
-sqlite_file_name = "database.db"
-sqlite_url = f"sqlite:///{sqlite_file_name}"
-
-connect_args = {"check_same_thread": False}
-engine = create_engine(sqlite_url, connect_args=connect_args)
-
-
-def create_db_and_tables():
- SQLModel.metadata.create_all(engine)
-
-
-def get_session():
- with Session(engine) as session:
- yield session
-
-
-SessionDep = Annotated[Session, Depends(get_session)]
-app = FastAPI()
-
-
-@app.on_event("startup")
-def on_startup():
- create_db_and_tables()
-
-
-@app.post("/heroes/", response_model=HeroPublic)
-def create_hero(hero: HeroCreate, session: SessionDep):
- db_hero = Hero.model_validate(hero)
- session.add(db_hero)
- session.commit()
- session.refresh(db_hero)
- return db_hero
-
-
-@app.get("/heroes/", response_model=List[HeroPublic])
-def read_heroes(
- session: SessionDep,
- offset: int = 0,
- limit: Annotated[int, Query(le=100)] = 100,
-):
- heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
- return heroes
-
-
-@app.get("/heroes/{hero_id}", response_model=HeroPublic)
-def read_hero(hero_id: int, session: SessionDep):
- hero = session.get(Hero, hero_id)
- if not hero:
- raise HTTPException(status_code=404, detail="Hero not found")
- return hero
-
-
-@app.patch("/heroes/{hero_id}", response_model=HeroPublic)
-def update_hero(hero_id: int, hero: HeroUpdate, session: SessionDep):
- hero_db = session.get(Hero, hero_id)
- if not hero_db:
- raise HTTPException(status_code=404, detail="Hero not found")
- hero_data = hero.model_dump(exclude_unset=True)
- hero_db.sqlmodel_update(hero_data)
- session.add(hero_db)
- session.commit()
- session.refresh(hero_db)
- return hero_db
-
-
-@app.delete("/heroes/{hero_id}")
-def delete_hero(hero_id: int, session: SessionDep):
- hero = session.get(Hero, hero_id)
- if not hero:
- raise HTTPException(status_code=404, detail="Hero not found")
- session.delete(hero)
- session.commit()
- return {"ok": True}
+++ /dev/null
-from typing import Union
-
-from fastapi import (
- Cookie,
- Depends,
- FastAPI,
- Query,
- WebSocket,
- WebSocketException,
- status,
-)
-from fastapi.responses import HTMLResponse
-from typing_extensions import Annotated
-
-app = FastAPI()
-
-html = """
-<!DOCTYPE html>
-<html>
- <head>
- <title>Chat</title>
- </head>
- <body>
- <h1>WebSocket Chat</h1>
- <form action="" onsubmit="sendMessage(event)">
- <label>Item ID: <input type="text" id="itemId" autocomplete="off" value="foo"/></label>
- <label>Token: <input type="text" id="token" autocomplete="off" value="some-key-token"/></label>
- <button onclick="connect(event)">Connect</button>
- <hr>
- <label>Message: <input type="text" id="messageText" autocomplete="off"/></label>
- <button>Send</button>
- </form>
- <ul id='messages'>
- </ul>
- <script>
- var ws = null;
- function connect(event) {
- var itemId = document.getElementById("itemId")
- var token = document.getElementById("token")
- ws = new WebSocket("ws://localhost:8000/items/" + itemId.value + "/ws?token=" + token.value);
- ws.onmessage = function(event) {
- var messages = document.getElementById('messages')
- var message = document.createElement('li')
- var content = document.createTextNode(event.data)
- message.appendChild(content)
- messages.appendChild(message)
- };
- event.preventDefault()
- }
- function sendMessage(event) {
- var input = document.getElementById("messageText")
- ws.send(input.value)
- input.value = ''
- event.preventDefault()
- }
- </script>
- </body>
-</html>
-"""
-
-
-@app.get("/")
-async def get():
- return HTMLResponse(html)
-
-
-async def get_cookie_or_token(
- websocket: WebSocket,
- session: Annotated[Union[str, None], Cookie()] = None,
- token: Annotated[Union[str, None], Query()] = None,
-):
- if session is None and token is None:
- raise WebSocketException(code=status.WS_1008_POLICY_VIOLATION)
- return session or token
-
-
-@app.websocket("/items/{item_id}/ws")
-async def websocket_endpoint(
- *,
- websocket: WebSocket,
- item_id: str,
- q: Union[int, None] = None,
- cookie_or_token: Annotated[str, Depends(get_cookie_or_token)],
-):
- await websocket.accept()
- while True:
- data = await websocket.receive_text()
- await websocket.send_text(
- f"Session cookie or query token value is: {cookie_or_token}"
- )
- if q is not None:
- await websocket.send_text(f"Query parameter q is: {q}")
- await websocket.send_text(f"Message text was: {data}, for item ID: {item_id}")
+++ /dev/null
-from typing import List
-
-from fastapi import FastAPI, WebSocket, WebSocketDisconnect
-from fastapi.responses import HTMLResponse
-
-app = FastAPI()
-
-html = """
-<!DOCTYPE html>
-<html>
- <head>
- <title>Chat</title>
- </head>
- <body>
- <h1>WebSocket Chat</h1>
- <h2>Your ID: <span id="ws-id"></span></h2>
- <form action="" onsubmit="sendMessage(event)">
- <input type="text" id="messageText" autocomplete="off"/>
- <button>Send</button>
- </form>
- <ul id='messages'>
- </ul>
- <script>
- var client_id = Date.now()
- document.querySelector("#ws-id").textContent = client_id;
- var ws = new WebSocket(`ws://localhost:8000/ws/${client_id}`);
- ws.onmessage = function(event) {
- var messages = document.getElementById('messages')
- var message = document.createElement('li')
- var content = document.createTextNode(event.data)
- message.appendChild(content)
- messages.appendChild(message)
- };
- function sendMessage(event) {
- var input = document.getElementById("messageText")
- ws.send(input.value)
- input.value = ''
- event.preventDefault()
- }
- </script>
- </body>
-</html>
-"""
-
-
-class ConnectionManager:
- def __init__(self):
- self.active_connections: List[WebSocket] = []
-
- async def connect(self, websocket: WebSocket):
- await websocket.accept()
- self.active_connections.append(websocket)
-
- def disconnect(self, websocket: WebSocket):
- self.active_connections.remove(websocket)
-
- async def send_personal_message(self, message: str, websocket: WebSocket):
- await websocket.send_text(message)
-
- async def broadcast(self, message: str):
- for connection in self.active_connections:
- await connection.send_text(message)
-
-
-manager = ConnectionManager()
-
-
-@app.get("/")
-async def get():
- return HTMLResponse(html)
-
-
-@app.websocket("/ws/{client_id}")
-async def websocket_endpoint(websocket: WebSocket, client_id: int):
- await manager.connect(websocket)
- try:
- while True:
- data = await websocket.receive_text()
- await manager.send_personal_message(f"You wrote: {data}", websocket)
- await manager.broadcast(f"Client #{client_id} says: {data}")
- except WebSocketDisconnect:
- manager.disconnect(websocket)
- await manager.broadcast(f"Client #{client_id} left the chat")
from typing_extensions import Annotated, get_args, get_origin
# Copy from Pydantic v2, compatible with v1
-if sys.version_info < (3, 9):
- # Pydantic no longer supports Python 3.8, this might be incorrect, but the code
- # this is used for is also never reached in this codebase, as it's a copy of
- # Pydantic's lenient_issubclass, just for compatibility with v1
- # TODO: remove when dropping support for Python 3.8
- WithArgsTypes: Tuple[Any, ...] = ()
-elif sys.version_info < (3, 10):
+if sys.version_info < (3, 10):
WithArgsTypes: tuple[Any, ...] = (typing._GenericAlias, types.GenericAlias) # type: ignore[attr-defined]
else:
WithArgsTypes: tuple[Any, ...] = (
context = '${CONTEXT}'
dynamic_context = "test_function"
omit = [
- "docs_src/response_model/tutorial003_04.py",
+ "docs_src/response_model/tutorial003_04_py39.py",
"docs_src/response_model/tutorial003_04_py310.py",
]
[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"]
-"docs_src/dependencies/tutorial007.py" = ["F821"]
-"docs_src/dependencies/tutorial008.py" = ["F821"]
-"docs_src/dependencies/tutorial009.py" = ["F821"]
-"docs_src/dependencies/tutorial010.py" = ["F821"]
-"docs_src/custom_response/tutorial007.py" = ["B007"]
-"docs_src/dataclasses/tutorial003.py" = ["I001"]
-"docs_src/path_operation_advanced_configuration/tutorial007.py" = ["B904"]
+"docs_src/dependencies/tutorial007_py39.py" = ["F821"]
+"docs_src/dependencies/tutorial008_py39.py" = ["F821"]
+"docs_src/dependencies/tutorial009_py39.py" = ["F821"]
+"docs_src/dependencies/tutorial010_py39.py" = ["F821"]
+"docs_src/custom_response/tutorial007_py39.py" = ["B007"]
+"docs_src/dataclasses/tutorial003_py39.py" = ["I001"]
"docs_src/path_operation_advanced_configuration/tutorial007_py39.py" = ["B904"]
-"docs_src/path_operation_advanced_configuration/tutorial007_pv1.py" = ["B904"]
"docs_src/path_operation_advanced_configuration/tutorial007_pv1_py39.py" = ["B904"]
-"docs_src/custom_request_and_route/tutorial002.py" = ["B904"]
"docs_src/custom_request_and_route/tutorial002_py39.py" = ["B904"]
"docs_src/custom_request_and_route/tutorial002_py310.py" = ["B904"]
-"docs_src/custom_request_and_route/tutorial002_an.py" = ["B904"]
"docs_src/custom_request_and_route/tutorial002_an_py39.py" = ["B904"]
"docs_src/custom_request_and_route/tutorial002_an_py310.py" = ["B904"]
-"docs_src/dependencies/tutorial008_an.py" = ["F821"]
"docs_src/dependencies/tutorial008_an_py39.py" = ["F821"]
-"docs_src/query_params_str_validations/tutorial012_an.py" = ["B006"]
"docs_src/query_params_str_validations/tutorial012_an_py39.py" = ["B006"]
-"docs_src/query_params_str_validations/tutorial013_an.py" = ["B006"]
"docs_src/query_params_str_validations/tutorial013_an_py39.py" = ["B006"]
-"docs_src/security/tutorial004.py" = ["B904"]
-"docs_src/security/tutorial004_an.py" = ["B904"]
-"docs_src/security/tutorial004_an_py310.py" = ["B904"]
+"docs_src/security/tutorial004_py39.py" = ["B904"]
"docs_src/security/tutorial004_an_py39.py" = ["B904"]
+"docs_src/security/tutorial004_an_py310.py" = ["B904"]
"docs_src/security/tutorial004_py310.py" = ["B904"]
-"docs_src/security/tutorial005.py" = ["B904"]
-"docs_src/security/tutorial005_an.py" = ["B904"]
"docs_src/security/tutorial005_an_py310.py" = ["B904"]
"docs_src/security/tutorial005_an_py39.py" = ["B904"]
"docs_src/security/tutorial005_py310.py" = ["B904"]
"docs_src/security/tutorial005_py39.py" = ["B904"]
-"docs_src/dependencies/tutorial008b.py" = ["B904"]
-"docs_src/dependencies/tutorial008b_an.py" = ["B904"]
+"docs_src/dependencies/tutorial008b_py39.py" = ["B904"]
"docs_src/dependencies/tutorial008b_an_py39.py" = ["B904"]
from fastapi.testclient import TestClient
-from docs_src.additional_responses.tutorial001 import app
+from docs_src.additional_responses.tutorial001_py39 import app
client = TestClient(app)
@pytest.fixture(
name="client",
params=[
- pytest.param("tutorial002"),
+ pytest.param("tutorial002_py39"),
pytest.param("tutorial002_py310", marks=needs_py310),
],
)
from fastapi.testclient import TestClient
-from docs_src.additional_responses.tutorial003 import app
+from docs_src.additional_responses.tutorial003_py39 import app
client = TestClient(app)
@pytest.fixture(
name="client",
params=[
- pytest.param("tutorial004"),
+ pytest.param("tutorial004_py39"),
pytest.param("tutorial004_py310", marks=needs_py310),
],
)
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial001",
+ "tutorial001_py39",
pytest.param("tutorial001_py310", marks=needs_py310),
- "tutorial001_an",
- pytest.param("tutorial001_an_py39", marks=needs_py39),
+ "tutorial001_an_py39",
pytest.param("tutorial001_an_py310", marks=needs_py310),
],
)
from fastapi.testclient import TestClient
-from docs_src.advanced_middleware.tutorial001 import app
+from docs_src.advanced_middleware.tutorial001_py39 import app
def test_middleware():
from fastapi.testclient import TestClient
-from docs_src.advanced_middleware.tutorial002 import app
+from docs_src.advanced_middleware.tutorial002_py39 import app
def test_middleware():
from fastapi.responses import PlainTextResponse
from fastapi.testclient import TestClient
-from docs_src.advanced_middleware.tutorial003 import app
+from docs_src.advanced_middleware.tutorial003_py39 import app
@app.get("/large")
import pytest
-from docs_src.async_tests.test_main import test_root
+from docs_src.async_tests.app_a_py39.test_main import test_root
@pytest.mark.anyio
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
-from ...utils import needs_py39
-
@pytest.fixture(
name="client",
params=[
- "tutorial001_an",
- pytest.param("tutorial001_an_py39", marks=needs_py39),
+ "tutorial001_an_py39",
],
)
def get_client(request: pytest.FixtureRequest):
from fastapi.testclient import TestClient
-from docs_src.background_tasks.tutorial001 import app
+from docs_src.background_tasks.tutorial001_py39 import app
client = TestClient(app)
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial002",
+ "tutorial002_py39",
pytest.param("tutorial002_py310", marks=needs_py310),
- "tutorial002_an",
- pytest.param("tutorial002_an_py39", marks=needs_py39),
+ "tutorial002_an_py39",
pytest.param("tutorial002_an_py310", marks=needs_py310),
],
)
from fastapi.testclient import TestClient
-from docs_src.behind_a_proxy.tutorial001 import app
+from docs_src.behind_a_proxy.tutorial001_py39 import app
client = TestClient(app, root_path="/api/v1")
from fastapi.testclient import TestClient
-from docs_src.behind_a_proxy.tutorial001_01 import app
+from docs_src.behind_a_proxy.tutorial001_01_py39 import app
client = TestClient(
app,
from fastapi.testclient import TestClient
-from docs_src.behind_a_proxy.tutorial002 import app
+from docs_src.behind_a_proxy.tutorial002_py39 import app
client = TestClient(app)
from dirty_equals import IsOneOf
from fastapi.testclient import TestClient
-from docs_src.behind_a_proxy.tutorial003 import app
+from docs_src.behind_a_proxy.tutorial003_py39 import app
client = TestClient(app)
from dirty_equals import IsOneOf
from fastapi.testclient import TestClient
-from docs_src.behind_a_proxy.tutorial004 import app
+from docs_src.behind_a_proxy.tutorial004_py39 import app
client = TestClient(app)
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39
-
@pytest.fixture(
name="client",
params=[
- "app_an.main",
- pytest.param("app_an_py39.main", marks=needs_py39),
- "app.main",
+ "app_py39.main",
+ "app_an_py39.main",
],
)
def get_client(request: pytest.FixtureRequest):
@pytest.fixture(
name="client",
params=[
- "tutorial001",
+ "tutorial001_py39",
pytest.param("tutorial001_py310", marks=needs_py310),
],
)
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial001",
+ "tutorial001_py39",
pytest.param("tutorial001_py310", marks=needs_py310),
- "tutorial001_an",
- pytest.param("tutorial001_an_py39", marks=needs_py39),
+ "tutorial001_an_py39",
pytest.param("tutorial001_an_py310", marks=needs_py310),
],
)
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial001",
+ "tutorial001_py39",
pytest.param("tutorial001_py310", marks=needs_py310),
- "tutorial001_an",
- pytest.param("tutorial001_an_py39", marks=needs_py39),
+ "tutorial001_an_py39",
pytest.param("tutorial001_an_py310", marks=needs_py310),
],
)
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial003",
+ "tutorial003_py39",
pytest.param("tutorial003_py310", marks=needs_py310),
- "tutorial003_an",
- pytest.param("tutorial003_an_py39", marks=needs_py39),
+ "tutorial003_an_py39",
pytest.param("tutorial003_an_py310", marks=needs_py310),
],
)
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39
-
@pytest.fixture(
name="client",
params=[
- "tutorial009",
- pytest.param("tutorial009_py39", marks=needs_py39),
+ "tutorial009_py39",
],
)
def get_client(request: pytest.FixtureRequest):
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310, needs_pydanticv1, needs_pydanticv2
+from ...utils import needs_py310, needs_pydanticv1, needs_pydanticv2
@pytest.fixture(
name="client",
params=[
- "tutorial001",
+ "tutorial001_py39",
pytest.param("tutorial001_py310", marks=needs_py310),
- pytest.param("tutorial001_py39", marks=needs_py39),
],
)
def get_client(request: pytest.FixtureRequest):
def get_client() -> TestClient:
- from docs_src.conditional_openapi import tutorial001
+ from docs_src.conditional_openapi import tutorial001_py39
- importlib.reload(tutorial001)
+ importlib.reload(tutorial001_py39)
- client = TestClient(tutorial001.app)
+ client = TestClient(tutorial001_py39.app)
return client
from fastapi.testclient import TestClient
-from docs_src.configure_swagger_ui.tutorial001 import app
+from docs_src.configure_swagger_ui.tutorial001_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.configure_swagger_ui.tutorial002 import app
+from docs_src.configure_swagger_ui.tutorial002_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.configure_swagger_ui.tutorial003 import app
+from docs_src.configure_swagger_ui.tutorial003_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
-from tests.utils import needs_py39, needs_py310
+from tests.utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial001",
+ "tutorial001_py39",
pytest.param("tutorial001_py310", marks=needs_py310),
- "tutorial001_an",
- pytest.param("tutorial001_an_py39", marks=needs_py39),
+ "tutorial001_an_py39",
pytest.param("tutorial001_an_py310", marks=needs_py310),
],
)
from inline_snapshot import snapshot
from tests.utils import (
- needs_py39,
needs_py310,
needs_pydanticv1,
needs_pydanticv2,
@pytest.fixture(
name="client",
params=[
- pytest.param("tutorial002", marks=needs_pydanticv2),
+ pytest.param("tutorial002_py39", marks=needs_pydanticv2),
pytest.param("tutorial002_py310", marks=[needs_py310, needs_pydanticv2]),
- pytest.param("tutorial002_an", marks=needs_pydanticv2),
- pytest.param("tutorial002_an_py39", marks=[needs_py39, needs_pydanticv2]),
+ pytest.param("tutorial002_an_py39", marks=needs_pydanticv2),
pytest.param("tutorial002_an_py310", marks=[needs_py310, needs_pydanticv2]),
- pytest.param("tutorial002_pv1", marks=[needs_pydanticv1, needs_pydanticv1]),
+ pytest.param("tutorial002_pv1_py39", marks=needs_pydanticv1),
pytest.param("tutorial002_pv1_py310", marks=[needs_py310, needs_pydanticv1]),
- pytest.param("tutorial002_pv1_an", marks=[needs_pydanticv1]),
- pytest.param("tutorial002_pv1_an_py39", marks=[needs_py39, needs_pydanticv1]),
+ pytest.param("tutorial002_pv1_an_py39", marks=needs_pydanticv1),
pytest.param("tutorial002_pv1_an_py310", marks=[needs_py310, needs_pydanticv1]),
],
)
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="mod",
params=[
- "tutorial001",
+ "tutorial001_py39",
pytest.param("tutorial001_py310", marks=needs_py310),
- "tutorial001_an",
- pytest.param("tutorial001_an_py39", marks=needs_py39),
+ "tutorial001_an_py39",
pytest.param("tutorial001_an_py310", marks=needs_py310),
],
)
from fastapi.testclient import TestClient
-from docs_src.cors.tutorial001 import app
+from docs_src.cors.tutorial001_py39 import app
def test_cors():
static_dir: Path = Path(os.getcwd()) / "static"
print(static_dir)
static_dir.mkdir(exist_ok=True)
- from docs_src.custom_docs_ui.tutorial001 import app
+ from docs_src.custom_docs_ui.tutorial001_py39 import app
with TestClient(app) as client:
yield client
static_dir: Path = Path(os.getcwd()) / "static"
print(static_dir)
static_dir.mkdir(exist_ok=True)
- from docs_src.custom_docs_ui.tutorial002 import app
+ from docs_src.custom_docs_ui.tutorial002_py39 import app
with TestClient(app) as client:
yield client
from fastapi import Request
from fastapi.testclient import TestClient
-from tests.utils import needs_py39, needs_py310
+from tests.utils import needs_py310
@pytest.fixture(
name="client",
params=[
- pytest.param("tutorial001"),
- pytest.param("tutorial001_py39", marks=needs_py39),
+ pytest.param("tutorial001_py39"),
pytest.param("tutorial001_py310", marks=needs_py310),
- pytest.param("tutorial001_an"),
- pytest.param("tutorial001_an_py39", marks=needs_py39),
+ pytest.param("tutorial001_an_py39"),
pytest.param("tutorial001_an_py310", marks=needs_py310),
],
)
from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
-from tests.utils import needs_py39, needs_py310
+from tests.utils import needs_py310
@pytest.fixture(
name="client",
params=[
- pytest.param("tutorial002"),
- pytest.param("tutorial002_py39", marks=needs_py39),
+ pytest.param("tutorial002_py39"),
pytest.param("tutorial002_py310", marks=needs_py310),
- pytest.param("tutorial002_an"),
- pytest.param("tutorial002_an_py39", marks=needs_py39),
+ pytest.param("tutorial002_an_py39"),
pytest.param("tutorial002_an_py310", marks=needs_py310),
],
)
@pytest.fixture(
name="client",
params=[
- pytest.param("tutorial003"),
+ pytest.param("tutorial003_py39"),
pytest.param("tutorial003_py310", marks=needs_py310),
],
)
from fastapi.testclient import TestClient
-from docs_src.custom_response.tutorial001 import app
+from docs_src.custom_response.tutorial001_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.custom_response.tutorial001b import app
+from docs_src.custom_response.tutorial001b_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.custom_response.tutorial004 import app
+from docs_src.custom_response.tutorial004_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.custom_response.tutorial005 import app
+from docs_src.custom_response.tutorial005_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.custom_response.tutorial006 import app
+from docs_src.custom_response.tutorial006_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.custom_response.tutorial006b import app
+from docs_src.custom_response.tutorial006b_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.custom_response.tutorial006c import app
+from docs_src.custom_response.tutorial006c_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.custom_response.tutorial007 import app
+from docs_src.custom_response.tutorial007_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.custom_response import tutorial008
-from docs_src.custom_response.tutorial008 import app
+from docs_src.custom_response import tutorial008_py39
+from docs_src.custom_response.tutorial008_py39 import app
client = TestClient(app)
def test_get(tmp_path: Path):
file_path: Path = tmp_path / "large-video-file.mp4"
- tutorial008.some_file_path = str(file_path)
+ tutorial008_py39.some_file_path = str(file_path)
test_content = b"Fake video bytes"
file_path.write_bytes(test_content)
response = client.get("/")
from fastapi.testclient import TestClient
-from docs_src.custom_response import tutorial009
-from docs_src.custom_response.tutorial009 import app
+from docs_src.custom_response import tutorial009_py39
+from docs_src.custom_response.tutorial009_py39 import app
client = TestClient(app)
def test_get(tmp_path: Path):
file_path: Path = tmp_path / "large-video-file.mp4"
- tutorial009.some_file_path = str(file_path)
+ tutorial009_py39.some_file_path = str(file_path)
test_content = b"Fake video bytes"
file_path.write_bytes(test_content)
response = client.get("/")
from fastapi.testclient import TestClient
-from docs_src.custom_response import tutorial009b
-from docs_src.custom_response.tutorial009b import app
+from docs_src.custom_response import tutorial009b_py39
+from docs_src.custom_response.tutorial009b_py39 import app
client = TestClient(app)
def test_get(tmp_path: Path):
file_path: Path = tmp_path / "large-video-file.mp4"
- tutorial009b.some_file_path = str(file_path)
+ tutorial009b_py39.some_file_path = str(file_path)
test_content = b"Fake video bytes"
file_path.write_bytes(test_content)
response = client.get("/")
from fastapi.testclient import TestClient
-from docs_src.custom_response.tutorial009c import app
+from docs_src.custom_response.tutorial009c_py39 import app
client = TestClient(app)
@pytest.fixture(
name="client",
params=[
- pytest.param("tutorial001"),
+ pytest.param("tutorial001_py39"),
pytest.param("tutorial001_py310", marks=needs_py310),
],
)
from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
-from tests.utils import needs_py39, needs_py310
+from tests.utils import needs_py310
@pytest.fixture(
name="client",
params=[
- pytest.param("tutorial002"),
- pytest.param("tutorial002_py39", marks=needs_py39),
+ pytest.param("tutorial002_py39"),
pytest.param("tutorial002_py310", marks=needs_py310),
],
)
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310, needs_pydanticv1, needs_pydanticv2
+from ...utils import needs_py310, needs_pydanticv1, needs_pydanticv2
@pytest.fixture(
name="client",
params=[
- pytest.param("tutorial003"),
- pytest.param("tutorial003_py39", marks=needs_py39),
+ pytest.param("tutorial003_py39"),
pytest.param("tutorial003_py310", marks=needs_py310),
],
)
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial001",
+ pytest.param("tutorial001_py39"),
pytest.param("tutorial001_py310", marks=needs_py310),
- "tutorial001_an",
- pytest.param("tutorial001_an_py39", marks=needs_py39),
+ pytest.param("tutorial001_an_py39"),
pytest.param("tutorial001_an_py310", marks=needs_py310),
],
)
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial004",
+ pytest.param("tutorial004_py39"),
pytest.param("tutorial004_py310", marks=needs_py310),
- "tutorial004_an",
- pytest.param("tutorial004_an_py39", marks=needs_py39),
+ pytest.param("tutorial004_an_py39"),
pytest.param("tutorial004_an_py310", marks=needs_py310),
],
)
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39
-
@pytest.fixture(
name="client",
params=[
- "tutorial006",
- "tutorial006_an",
- pytest.param("tutorial006_an_py39", marks=needs_py39),
+ pytest.param("tutorial006_py39"),
+ pytest.param("tutorial006_an_py39"),
],
)
def get_client(request: pytest.FixtureRequest):
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39
-
@pytest.fixture(
name="client",
params=[
- "tutorial008b",
- "tutorial008b_an",
- pytest.param("tutorial008b_an_py39", marks=needs_py39),
+ pytest.param("tutorial008b_py39"),
+ pytest.param("tutorial008b_an_py39"),
],
)
def get_client(request: pytest.FixtureRequest):
from fastapi.exceptions import FastAPIError
from fastapi.testclient import TestClient
-from ...utils import needs_py39
-
@pytest.fixture(
name="mod",
params=[
- "tutorial008c",
- "tutorial008c_an",
- pytest.param("tutorial008c_an_py39", marks=needs_py39),
+ pytest.param("tutorial008c_py39"),
+ pytest.param("tutorial008c_an_py39"),
],
)
def get_mod(request: pytest.FixtureRequest):
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39
-
@pytest.fixture(
name="mod",
params=[
- "tutorial008d",
- "tutorial008d_an",
- pytest.param("tutorial008d_an_py39", marks=needs_py39),
+ pytest.param("tutorial008d_py39"),
+ pytest.param("tutorial008d_an_py39"),
],
)
def get_mod(request: pytest.FixtureRequest):
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39
-
@pytest.fixture(
name="client",
params=[
- "tutorial008e",
- "tutorial008e_an",
- pytest.param("tutorial008e_an_py39", marks=needs_py39),
+ pytest.param("tutorial008e_py39"),
+ pytest.param("tutorial008e_an_py39"),
],
)
def get_client(request: pytest.FixtureRequest):
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39
-
@pytest.fixture(
name="client",
params=[
- "tutorial012",
- "tutorial012_an",
- pytest.param("tutorial012_an_py39", marks=needs_py39),
+ pytest.param("tutorial012_py39"),
+ pytest.param("tutorial012_an_py39"),
],
)
def get_client(request: pytest.FixtureRequest):
@pytest.fixture(name="app", scope="module")
def get_app():
with pytest.warns(DeprecationWarning):
- from docs_src.events.tutorial001 import app
+ from docs_src.events.tutorial001_py39 import app
yield app
@pytest.fixture(name="app", scope="module")
def get_app():
with pytest.warns(DeprecationWarning):
- from docs_src.events.tutorial002 import app
+ from docs_src.events.tutorial002_py39 import app
yield app
from fastapi.testclient import TestClient
-from docs_src.events.tutorial003 import (
+from docs_src.events.tutorial003_py39 import (
app,
fake_answer_to_everything_ml_model,
ml_models,
from fastapi.testclient import TestClient
-from docs_src.extending_openapi.tutorial001 import app
+from docs_src.extending_openapi.tutorial001_py39 import app
client = TestClient(app)
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial001",
+ pytest.param("tutorial001_py39"),
pytest.param("tutorial001_py310", marks=needs_py310),
- "tutorial001_an",
- pytest.param("tutorial001_an_py39", marks=needs_py39),
+ pytest.param("tutorial001_an_py39"),
pytest.param("tutorial001_an_py310", marks=needs_py310),
],
)
@pytest.fixture(
name="client",
params=[
- "tutorial003",
+ pytest.param("tutorial003_py39"),
pytest.param("tutorial003_py310", marks=needs_py310),
],
)
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39
-
@pytest.fixture(
name="client",
params=[
- "tutorial004",
- pytest.param("tutorial004_py39", marks=needs_py39),
+ pytest.param("tutorial004_py39"),
],
)
def get_client(request: pytest.FixtureRequest):
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39
-
@pytest.fixture(
name="client",
params=[
- "tutorial005",
- pytest.param("tutorial005_py39", marks=needs_py39),
+ pytest.param("tutorial005_py39"),
],
)
def get_client(request: pytest.FixtureRequest):
import pytest
from fastapi.testclient import TestClient
-from docs_src.first_steps.tutorial001 import app
+from docs_src.first_steps.tutorial001_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.generate_clients.tutorial003 import app
+from docs_src.generate_clients.tutorial003_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.handling_errors.tutorial001 import app
+from docs_src.handling_errors.tutorial001_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.handling_errors.tutorial002 import app
+from docs_src.handling_errors.tutorial002_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.handling_errors.tutorial003 import app
+from docs_src.handling_errors.tutorial003_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.handling_errors.tutorial004 import app
+from docs_src.handling_errors.tutorial004_py39 import app
client = TestClient(app)
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from docs_src.handling_errors.tutorial005 import app
+from docs_src.handling_errors.tutorial005_py39 import app
client = TestClient(app)
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from docs_src.handling_errors.tutorial006 import app
+from docs_src.handling_errors.tutorial006_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
-from tests.utils import needs_py39, needs_py310
+from tests.utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial001",
- pytest.param("tutorial001_py39", marks=needs_py39),
+ pytest.param("tutorial001_py39"),
pytest.param("tutorial001_py310", marks=needs_py310),
- "tutorial001_an",
- pytest.param("tutorial001_an_py39", marks=needs_py39),
+ pytest.param("tutorial001_an_py39"),
pytest.param("tutorial001_an_py310", marks=needs_py310),
],
)
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
-from tests.utils import needs_py39, needs_py310, needs_pydanticv1, needs_pydanticv2
+from tests.utils import needs_py310, needs_pydanticv1, needs_pydanticv2
@pytest.fixture(
name="client",
params=[
- pytest.param("tutorial002", marks=needs_pydanticv2),
+ pytest.param("tutorial002_py39", marks=needs_pydanticv2),
pytest.param("tutorial002_py310", marks=[needs_py310, needs_pydanticv2]),
- pytest.param("tutorial002_an", marks=needs_pydanticv2),
- pytest.param("tutorial002_an_py39", marks=[needs_py39, needs_pydanticv2]),
+ pytest.param("tutorial002_an_py39", marks=needs_pydanticv2),
pytest.param("tutorial002_an_py310", marks=[needs_py310, needs_pydanticv2]),
- pytest.param("tutorial002_pv1", marks=[needs_pydanticv1, needs_pydanticv1]),
+ pytest.param("tutorial002_pv1_py39", marks=needs_pydanticv1),
pytest.param("tutorial002_pv1_py310", marks=[needs_py310, needs_pydanticv1]),
- pytest.param("tutorial002_pv1_an", marks=[needs_pydanticv1]),
- pytest.param("tutorial002_pv1_an_py39", marks=[needs_py39, needs_pydanticv1]),
+ pytest.param("tutorial002_pv1_an_py39", marks=needs_pydanticv1),
pytest.param("tutorial002_pv1_an_py310", marks=[needs_py310, needs_pydanticv1]),
],
)
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
-from tests.utils import needs_py39, needs_py310
+from tests.utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial003",
- pytest.param("tutorial003_py39", marks=needs_py39),
+ pytest.param("tutorial003_py39"),
pytest.param("tutorial003_py310", marks=needs_py310),
- "tutorial003_an",
- pytest.param("tutorial003_an_py39", marks=needs_py39),
+ pytest.param("tutorial003_an_py39"),
pytest.param("tutorial003_an_py310", marks=needs_py310),
],
)
@pytest.fixture(
name="client",
params=[
- "tutorial001",
+ pytest.param("tutorial001_py39"),
pytest.param("tutorial001_py310", marks=needs_py310),
- "tutorial001_an",
+ pytest.param("tutorial001_an_py39"),
pytest.param("tutorial001_an_py310", marks=needs_py310),
],
)
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial002",
+ pytest.param("tutorial002_py39"),
pytest.param("tutorial002_py310", marks=needs_py310),
- "tutorial002_an",
- pytest.param("tutorial002_an_py39", marks=needs_py39),
+ pytest.param("tutorial002_an_py39"),
pytest.param("tutorial002_an_py310", marks=needs_py310),
],
)
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial003",
+ pytest.param("tutorial003_py39"),
pytest.param("tutorial003_py310", marks=needs_py310),
- "tutorial003_an",
- pytest.param("tutorial003_an_py39", marks=needs_py39),
+ pytest.param("tutorial003_an_py39"),
pytest.param("tutorial003_an_py310", marks=needs_py310),
],
)
from fastapi.testclient import TestClient
-from docs_src.metadata.tutorial001 import app
+from docs_src.metadata.tutorial001_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.metadata.tutorial001_1 import app
+from docs_src.metadata.tutorial001_1_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.metadata.tutorial004 import app
+from docs_src.metadata.tutorial004_py39 import app
client = TestClient(app)
@pytest.fixture(
name="mod",
params=[
- pytest.param("tutorial001"),
+ pytest.param("tutorial001_py39"),
pytest.param("tutorial001_py310", marks=needs_py310),
],
)
from fastapi.testclient import TestClient
-from docs_src.openapi_webhooks.tutorial001 import app
+from docs_src.openapi_webhooks.tutorial001_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.path_operation_advanced_configuration.tutorial001 import app
+from docs_src.path_operation_advanced_configuration.tutorial001_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.path_operation_advanced_configuration.tutorial002 import app
+from docs_src.path_operation_advanced_configuration.tutorial002_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.path_operation_advanced_configuration.tutorial003 import app
+from docs_src.path_operation_advanced_configuration.tutorial003_py39 import app
client = TestClient(app)
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310, needs_pydanticv1, needs_pydanticv2
+from ...utils import needs_py310, needs_pydanticv1, needs_pydanticv2
@pytest.fixture(
name="client",
params=[
- pytest.param("tutorial004"),
- pytest.param("tutorial004_py39", marks=needs_py39),
+ pytest.param("tutorial004_py39"),
pytest.param("tutorial004_py310", marks=needs_py310),
],
)
from fastapi.testclient import TestClient
-from docs_src.path_operation_advanced_configuration.tutorial005 import app
+from docs_src.path_operation_advanced_configuration.tutorial005_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.path_operation_advanced_configuration.tutorial006 import app
+from docs_src.path_operation_advanced_configuration.tutorial006_py39 import app
client = TestClient(app)
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_pydanticv2
+from ...utils import needs_pydanticv2
@pytest.fixture(
name="client",
params=[
- pytest.param("tutorial007"),
- pytest.param("tutorial007_py39", marks=needs_py39),
+ pytest.param("tutorial007_py39"),
],
)
def get_client(request: pytest.FixtureRequest):
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_pydanticv1
+from ...utils import needs_pydanticv1
@pytest.fixture(
name="client",
params=[
- pytest.param("tutorial007_pv1"),
- pytest.param("tutorial007_pv1_py39", marks=needs_py39),
+ pytest.param("tutorial007_pv1_py39"),
],
)
def get_client(request: pytest.FixtureRequest):
from fastapi.testclient import TestClient
-from docs_src.path_operation_configuration.tutorial002b import app
+from docs_src.path_operation_configuration.tutorial002b_py39 import app
client = TestClient(app)
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310, needs_pydanticv1, needs_pydanticv2
+from ...utils import needs_py310, needs_pydanticv1, needs_pydanticv2
@pytest.fixture(
name="client",
params=[
- "tutorial005",
- pytest.param("tutorial005_py39", marks=needs_py39),
+ pytest.param("tutorial005_py39"),
pytest.param("tutorial005_py310", marks=needs_py310),
],
)
import pytest
from fastapi.testclient import TestClient
-from docs_src.path_operation_configuration.tutorial006 import app
+from docs_src.path_operation_configuration.tutorial006_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.path_params.tutorial004 import app
+from docs_src.path_params.tutorial004_py39 import app
client = TestClient(app)
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from docs_src.path_params.tutorial005 import app
+from docs_src.path_params.tutorial005_py39 import app
client = TestClient(app)
@pytest.fixture(
name="mod",
params=[
- "tutorial001_an",
+ "tutorial001_an_py39",
pytest.param("tutorial001_an_py310", marks=needs_py310),
],
)
@pytest.fixture(
name="client",
params=[
- "tutorial002_an",
+ "tutorial002_an_py39",
pytest.param("tutorial002_an_py310", marks=needs_py310),
],
)
@pytest.fixture(
name="client",
params=[
- "tutorial003_an",
+ "tutorial003_an_py39",
pytest.param("tutorial003_an_py310", marks=needs_py310),
],
)
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial004_an",
- pytest.param("tutorial004_an_py39", marks=needs_py39),
+ pytest.param("tutorial004_an_py39"),
pytest.param("tutorial004_an_py310", marks=needs_py310),
],
)
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
-from tests.utils import needs_py39, needs_py310
+from tests.utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial001",
- pytest.param("tutorial001_py39", marks=needs_py39),
+ pytest.param("tutorial001_py39"),
pytest.param("tutorial001_py310", marks=needs_py310),
- "tutorial001_an",
- pytest.param("tutorial001_an_py39", marks=needs_py39),
+ pytest.param("tutorial001_an_py39"),
pytest.param("tutorial001_an_py310", marks=needs_py310),
],
)
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
-from tests.utils import needs_py39, needs_py310, needs_pydanticv1, needs_pydanticv2
+from tests.utils import needs_py310, needs_pydanticv1, needs_pydanticv2
@pytest.fixture(
name="client",
params=[
- pytest.param("tutorial002", marks=needs_pydanticv2),
- pytest.param("tutorial002_py39", marks=[needs_py39, needs_pydanticv2]),
+ pytest.param("tutorial002_py39", marks=needs_pydanticv2),
pytest.param("tutorial002_py310", marks=[needs_py310, needs_pydanticv2]),
- pytest.param("tutorial002_an", marks=needs_pydanticv2),
- pytest.param("tutorial002_an_py39", marks=[needs_py39, needs_pydanticv2]),
+ pytest.param("tutorial002_an_py39", marks=needs_pydanticv2),
pytest.param("tutorial002_an_py310", marks=[needs_py310, needs_pydanticv2]),
- pytest.param("tutorial002_pv1", marks=[needs_pydanticv1, needs_pydanticv1]),
- pytest.param("tutorial002_pv1_py39", marks=[needs_py39, needs_pydanticv1]),
+ pytest.param("tutorial002_pv1_py39", marks=needs_pydanticv1),
pytest.param("tutorial002_pv1_py310", marks=[needs_py310, needs_pydanticv1]),
- pytest.param("tutorial002_pv1_an", marks=[needs_pydanticv1]),
- pytest.param("tutorial002_pv1_an_py39", marks=[needs_py39, needs_pydanticv1]),
+ pytest.param("tutorial002_pv1_an_py39", marks=needs_pydanticv1),
pytest.param("tutorial002_pv1_an_py310", marks=[needs_py310, needs_pydanticv1]),
],
)
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from docs_src.query_params.tutorial005 import app
+from docs_src.query_params.tutorial005_py39 import app
client = TestClient(app)
@pytest.fixture(
name="client",
params=[
- "tutorial006",
+ pytest.param("tutorial006_py39"),
pytest.param("tutorial006_py310", marks=needs_py310),
],
)
from fastapi._compat import PYDANTIC_VERSION_MINOR_TUPLE
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial010",
+ pytest.param("tutorial010_py39"),
pytest.param("tutorial010_py310", marks=needs_py310),
- "tutorial010_an",
- pytest.param("tutorial010_an_py39", marks=needs_py39),
+ pytest.param("tutorial010_an_py39"),
pytest.param("tutorial010_an_py310", marks=needs_py310),
],
)
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial011",
- pytest.param("tutorial011_py39", marks=needs_py310),
+ pytest.param("tutorial011_py39"),
pytest.param("tutorial011_py310", marks=needs_py310),
- "tutorial011_an",
- pytest.param("tutorial011_an_py39", marks=needs_py39),
+ pytest.param("tutorial011_an_py39"),
pytest.param("tutorial011_an_py310", marks=needs_py310),
],
)
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39
-
@pytest.fixture(
name="client",
params=[
- "tutorial012",
- pytest.param("tutorial012_py39", marks=needs_py39),
- "tutorial012_an",
- pytest.param("tutorial012_an_py39", marks=needs_py39),
+ pytest.param("tutorial012_py39"),
+ pytest.param("tutorial012_an_py39"),
],
)
def get_client(request: pytest.FixtureRequest):
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39
-
@pytest.fixture(
name="client",
params=[
- "tutorial013",
- "tutorial013_an",
- pytest.param("tutorial013_an_py39", marks=needs_py39),
+ "tutorial013_py39",
+ "tutorial013_an_py39",
],
)
def get_client(request: pytest.FixtureRequest):
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial014",
+ pytest.param("tutorial014_py39"),
pytest.param("tutorial014_py310", marks=needs_py310),
- "tutorial014_an",
- pytest.param("tutorial014_an_py39", marks=needs_py39),
+ pytest.param("tutorial014_an_py39"),
pytest.param("tutorial014_an_py310", marks=needs_py310),
],
)
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
-from ...utils import needs_py39, needs_py310, needs_pydanticv2
+from ...utils import needs_py310, needs_pydanticv2
@pytest.fixture(
name="client",
params=[
- pytest.param("tutorial015_an", marks=needs_pydanticv2),
+ pytest.param("tutorial015_an_py39", marks=needs_pydanticv2),
pytest.param("tutorial015_an_py310", marks=(needs_py310, needs_pydanticv2)),
- pytest.param("tutorial015_an_py39", marks=(needs_py39, needs_pydanticv2)),
],
)
def get_client(request: pytest.FixtureRequest):
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39
-
@pytest.fixture(
name="client",
params=[
- "tutorial001",
- "tutorial001_an",
- pytest.param("tutorial001_an_py39", marks=needs_py39),
+ "tutorial001_py39",
+ "tutorial001_an_py39",
],
)
def get_client(request: pytest.FixtureRequest):
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial001_02",
+ pytest.param("tutorial001_02_py39"),
pytest.param("tutorial001_02_py310", marks=needs_py310),
- "tutorial001_02_an",
- pytest.param("tutorial001_02_an_py39", marks=needs_py39),
+ pytest.param("tutorial001_02_an_py39"),
pytest.param("tutorial001_02_an_py310", marks=needs_py310),
],
)
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39
-
@pytest.fixture(
name="client",
params=[
- "tutorial001_03",
- "tutorial001_03_an",
- pytest.param("tutorial001_03_an_py39", marks=needs_py39),
+ "tutorial001_03_py39",
+ "tutorial001_03_an_py39",
],
)
def get_client(request: pytest.FixtureRequest):
from fastapi import FastAPI
from fastapi.testclient import TestClient
-from ...utils import needs_py39
-
@pytest.fixture(
name="app",
params=[
- "tutorial002",
- "tutorial002_an",
- pytest.param("tutorial002_py39", marks=needs_py39),
- pytest.param("tutorial002_an_py39", marks=needs_py39),
+ "tutorial002_py39",
+ "tutorial002_an_py39",
],
)
def get_app(request: pytest.FixtureRequest):
from fastapi import FastAPI
from fastapi.testclient import TestClient
-from ...utils import needs_py39
-
@pytest.fixture(
name="app",
params=[
- "tutorial003",
- "tutorial003_an",
- pytest.param("tutorial003_py39", marks=needs_py39),
- pytest.param("tutorial003_an_py39", marks=needs_py39),
+ "tutorial003_py39",
+ "tutorial003_an_py39",
],
)
def get_app(request: pytest.FixtureRequest):
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39
-
@pytest.fixture(
name="client",
params=[
- "tutorial001",
- "tutorial001_an",
- pytest.param("tutorial001_an_py39", marks=needs_py39),
+ "tutorial001_py39",
+ "tutorial001_an_py39",
],
)
def get_client(request: pytest.FixtureRequest):
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_pydanticv2
+from ...utils import needs_pydanticv2
@pytest.fixture(
name="client",
params=[
- "tutorial002",
- "tutorial002_an",
- pytest.param("tutorial002_an_py39", marks=needs_py39),
+ "tutorial002_py39",
+ "tutorial002_an_py39",
],
)
def get_client(request: pytest.FixtureRequest):
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_pydanticv1
+from ...utils import needs_pydanticv1
@pytest.fixture(
name="client",
params=[
- "tutorial002_pv1",
- "tutorial002_pv1_an",
- pytest.param("tutorial002_pv1_an_py39", marks=needs_py39),
+ "tutorial002_pv1_py39",
+ "tutorial002_pv1_an_py39",
],
)
def get_client(request: pytest.FixtureRequest):
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39
-
@pytest.fixture(
name="client",
params=[
- "tutorial001",
- "tutorial001_an",
- pytest.param("tutorial001_an_py39", marks=needs_py39),
+ "tutorial001_py39",
+ "tutorial001_an_py39",
],
)
def get_client(request: pytest.FixtureRequest):
from fastapi import FastAPI
from fastapi.testclient import TestClient
-from ...utils import needs_py39
-
@pytest.fixture(
name="app",
params=[
- "tutorial001",
- "tutorial001_an",
- pytest.param("tutorial001_an_py39", marks=needs_py39),
+ "tutorial001_py39",
+ "tutorial001_an_py39",
],
)
def get_app(request: pytest.FixtureRequest):
from fastapi.testclient import TestClient
-from docs_src.response_change_status_code.tutorial001 import app
+from docs_src.response_change_status_code.tutorial001_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.response_cookies.tutorial001 import app
+from docs_src.response_cookies.tutorial001_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.response_cookies.tutorial002 import app
+from docs_src.response_cookies.tutorial002_py39 import app
client = TestClient(app)
@pytest.fixture(
name="client",
params=[
- pytest.param("tutorial001"),
+ pytest.param("tutorial001_py39"),
pytest.param("tutorial001_py310", marks=needs_py310),
],
)
from fastapi.testclient import TestClient
-from docs_src.response_headers.tutorial001 import app
+from docs_src.response_headers.tutorial001_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.response_headers.tutorial002 import app
+from docs_src.response_headers.tutorial002_py39 import app
client = TestClient(app)
@pytest.fixture(
name="client",
params=[
- "tutorial003",
+ pytest.param("tutorial003_py39"),
pytest.param("tutorial003_py310", marks=needs_py310),
],
)
@pytest.fixture(
name="client",
params=[
- "tutorial003_01",
+ pytest.param("tutorial003_01_py39"),
pytest.param("tutorial003_01_py310", marks=needs_py310),
],
)
from fastapi.testclient import TestClient
-from docs_src.response_model.tutorial003_02 import app
+from docs_src.response_model.tutorial003_02_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
-from docs_src.response_model.tutorial003_03 import app
+from docs_src.response_model.tutorial003_03_py39 import app
client = TestClient(app)
@pytest.mark.parametrize(
"module_name",
[
- "tutorial003_04",
+ pytest.param("tutorial003_04_py39"),
pytest.param("tutorial003_04_py310", marks=needs_py310),
],
)
@pytest.fixture(
name="client",
params=[
- "tutorial003_05",
+ pytest.param("tutorial003_05_py39"),
pytest.param("tutorial003_05_py310", marks=needs_py310),
],
)
from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial004",
- pytest.param("tutorial004_py39", marks=needs_py39),
+ pytest.param("tutorial004_py39"),
pytest.param("tutorial004_py310", marks=needs_py310),
],
)
@pytest.fixture(
name="client",
params=[
- "tutorial005",
+ pytest.param("tutorial005_py39"),
pytest.param("tutorial005_py310", marks=needs_py310),
],
)
@pytest.fixture(
name="client",
params=[
- "tutorial006",
+ pytest.param("tutorial006_py39"),
pytest.param("tutorial006_py310", marks=needs_py310),
],
)
@pytest.fixture(
name="client",
params=[
- "tutorial001",
+ pytest.param("tutorial001_py39"),
pytest.param("tutorial001_py310", marks=needs_py310),
],
)
@pytest.fixture(
name="client",
params=[
- "tutorial001_pv1",
+ pytest.param("tutorial001_pv1_py39"),
pytest.param("tutorial001_pv1_py310", marks=needs_py310),
],
)
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial004",
+ pytest.param("tutorial004_py39"),
pytest.param("tutorial004_py310", marks=needs_py310),
- "tutorial004_an",
- pytest.param("tutorial004_an_py39", marks=needs_py39),
+ pytest.param("tutorial004_an_py39"),
pytest.param("tutorial004_an_py310", marks=needs_py310),
],
)
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial005",
+ pytest.param("tutorial005_py39"),
pytest.param("tutorial005_py310", marks=needs_py310),
- "tutorial005_an",
- pytest.param("tutorial005_an_py39", marks=needs_py39),
+ pytest.param("tutorial005_an_py39"),
pytest.param("tutorial005_an_py310", marks=needs_py310),
],
)
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39
-
@pytest.fixture(
name="client",
params=[
- "tutorial001",
- "tutorial001_an",
- pytest.param("tutorial001_an_py39", marks=needs_py39),
+ pytest.param("tutorial001_py39"),
+ pytest.param("tutorial001_an_py39"),
],
)
def get_client(request: pytest.FixtureRequest):
from dirty_equals import IsDict
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="client",
params=[
- "tutorial003",
+ pytest.param("tutorial003_py39"),
pytest.param("tutorial003_py310", marks=needs_py310),
- "tutorial003_an",
- pytest.param("tutorial003_an_py39", marks=needs_py39),
+ pytest.param("tutorial003_an_py39"),
pytest.param("tutorial003_an_py310", marks=needs_py310),
],
)
from dirty_equals import IsDict, IsOneOf
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="mod",
params=[
- "tutorial005",
+ pytest.param("tutorial005_py39"),
pytest.param("tutorial005_py310", marks=needs_py310),
- "tutorial005_an",
- pytest.param("tutorial005_py39", marks=needs_py39),
- pytest.param("tutorial005_an_py39", marks=needs_py39),
+ pytest.param("tutorial005_an_py39"),
pytest.param("tutorial005_an_py310", marks=needs_py310),
],
)
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39
-
@pytest.fixture(
name="client",
params=[
- "tutorial006",
- "tutorial006_an",
- pytest.param("tutorial006_an_py39", marks=needs_py39),
+ pytest.param("tutorial006_py39"),
+ pytest.param("tutorial006_an_py39"),
],
)
def get_client(request: pytest.FixtureRequest):
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310, needs_pydanticv2
+from ...utils import needs_py310, needs_pydanticv2
@pytest.fixture(
name="client",
params=[
- "tutorial001",
+ pytest.param("tutorial001_py39"),
pytest.param("tutorial001_py310", marks=needs_py310),
- pytest.param("tutorial001_py39", marks=needs_py39),
],
)
def get_client(request: pytest.FixtureRequest) -> TestClient:
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39, needs_py310, needs_pydanticv2
+from ...utils import needs_py310, needs_pydanticv2
@pytest.fixture(
name="client",
params=[
- "tutorial002",
+ pytest.param("tutorial002_py39"),
pytest.param("tutorial002_py310", marks=needs_py310),
- pytest.param("tutorial002_py39", marks=needs_py39),
],
)
def get_client(request: pytest.FixtureRequest) -> TestClient:
import pytest
from pytest import MonkeyPatch
-from ...utils import needs_py39, needs_pydanticv2
+from ...utils import needs_pydanticv2
@pytest.fixture(
name="mod_path",
params=[
- pytest.param("app02"),
- pytest.param("app02_an"),
- pytest.param("app02_an_py39", marks=needs_py39),
+ pytest.param("app02_py39"),
+ pytest.param("app02_an_py39"),
],
)
def get_mod_path(request: pytest.FixtureRequest):
from fastapi.testclient import TestClient
from pytest import MonkeyPatch
-from ...utils import needs_py39, needs_pydanticv1, needs_pydanticv2
+from ...utils import needs_pydanticv1, needs_pydanticv2
@pytest.fixture(
name="mod_path",
params=[
- pytest.param("app03"),
- pytest.param("app03_an"),
- pytest.param("app03_an_py39", marks=needs_py39),
+ pytest.param("app03_py39"),
+ pytest.param("app03_an_py39"),
],
)
def get_mod_path(request: pytest.FixtureRequest):
@pytest.fixture(
name="app",
params=[
- pytest.param("tutorial001", marks=needs_pydanticv2),
- pytest.param("tutorial001_pv1", marks=needs_pydanticv1),
+ pytest.param("tutorial001_py39", marks=needs_pydanticv2),
+ pytest.param("tutorial001_pv1_py39", marks=needs_pydanticv1),
],
)
def get_app(request: pytest.FixtureRequest, monkeypatch: MonkeyPatch):
from sqlmodel import SQLModel, create_engine
from sqlmodel.main import default_registry
-from tests.utils import needs_py39, needs_py310
+from tests.utils import needs_py310
def clear_sqlmodel():
@pytest.fixture(
name="client",
params=[
- "tutorial001",
- pytest.param("tutorial001_py39", marks=needs_py39),
+ pytest.param("tutorial001_py39"),
pytest.param("tutorial001_py310", marks=needs_py310),
- "tutorial001_an",
- pytest.param("tutorial001_an_py39", marks=needs_py39),
+ pytest.param("tutorial001_an_py39"),
pytest.param("tutorial001_an_py310", marks=needs_py310),
],
)
from sqlmodel import SQLModel, create_engine
from sqlmodel.main import default_registry
-from tests.utils import needs_py39, needs_py310
+from tests.utils import needs_py310
def clear_sqlmodel():
@pytest.fixture(
name="client",
params=[
- "tutorial002",
- pytest.param("tutorial002_py39", marks=needs_py39),
+ pytest.param("tutorial002_py39"),
pytest.param("tutorial002_py310", marks=needs_py310),
- "tutorial002_an",
- pytest.param("tutorial002_an_py39", marks=needs_py39),
+ pytest.param("tutorial002_an_py39"),
pytest.param("tutorial002_an_py310", marks=needs_py310),
],
)
from fastapi.testclient import TestClient
-from docs_src.sub_applications.tutorial001 import app
+from docs_src.sub_applications.tutorial001_py39 import app
client = TestClient(app)
shutil.rmtree("./templates")
shutil.copytree("./docs_src/templates/templates/", "./templates")
shutil.copytree("./docs_src/templates/static/", "./static")
- from docs_src.templates.tutorial001 import app
+ from docs_src.templates.tutorial001_py39 import app
client = TestClient(app)
response = client.get("/items/foo")
-from docs_src.app_testing.test_main import client, test_read_main
+from docs_src.app_testing.app_a_py39.test_main import client, test_read_main
def test_main():
import pytest
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="test_module",
params=[
- "app_b.test_main",
+ "app_b_py39.test_main",
pytest.param("app_b_py310.test_main", marks=needs_py310),
- "app_b_an.test_main",
- pytest.param("app_b_an_py39.test_main", marks=needs_py39),
+ "app_b_an_py39.test_main",
pytest.param("app_b_an_py310.test_main", marks=needs_py310),
],
)
-from docs_src.app_testing.tutorial001 import client, test_read_main
+from docs_src.app_testing.tutorial001_py39 import client, test_read_main
def test_main():
-from docs_src.app_testing.tutorial002 import test_read_main, test_websocket
+from docs_src.app_testing.tutorial002_py39 import test_read_main, test_websocket
def test_main():
def test_main():
with pytest.warns(DeprecationWarning):
- from docs_src.app_testing.tutorial003 import test_read_items
+ from docs_src.app_testing.tutorial003_py39 import test_read_items
test_read_items()
-from docs_src.app_testing.tutorial004 import test_read_items
+from docs_src.app_testing.tutorial004_py39 import test_read_items
def test_main():
import pytest
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="test_module",
params=[
- "tutorial001",
+ pytest.param("tutorial001_py39"),
pytest.param("tutorial001_py310", marks=needs_py310),
- "tutorial001_an",
- pytest.param("tutorial001_an_py39", marks=needs_py39),
+ pytest.param("tutorial001_an_py39"),
pytest.param("tutorial001_an_py310", marks=needs_py310),
],
)
from fastapi.testclient import TestClient
-from docs_src.using_request_directly.tutorial001 import app
+from docs_src.using_request_directly.tutorial001_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
from fastapi.websockets import WebSocketDisconnect
-from docs_src.websockets.tutorial001 import app
+from docs_src.websockets.tutorial001_py39 import app
client = TestClient(app)
from fastapi.testclient import TestClient
from fastapi.websockets import WebSocketDisconnect
-from ...utils import needs_py39, needs_py310
+from ...utils import needs_py310
@pytest.fixture(
name="app",
params=[
- "tutorial002",
+ pytest.param("tutorial002_py39"),
pytest.param("tutorial002_py310", marks=needs_py310),
- "tutorial002_an",
- pytest.param("tutorial002_an_py39", marks=needs_py39),
+ pytest.param("tutorial002_an_py39"),
pytest.param("tutorial002_an_py310", marks=needs_py310),
],
)
import pytest
from fastapi.testclient import TestClient
-from ...utils import needs_py39
-
@pytest.fixture(
name="mod",
params=[
- pytest.param("tutorial003"),
- pytest.param("tutorial003_py39", marks=needs_py39),
+ pytest.param("tutorial003_py39"),
],
)
def get_mod(request: pytest.FixtureRequest):
return client
-@needs_py39
def test_get(client: TestClient, html: str):
response = client.get("/")
assert response.text == html
-@needs_py39
def test_websocket_handle_disconnection(client: TestClient):
with client.websocket_connect("/ws/1234") as connection, client.websocket_connect(
"/ws/5678"
from fastapi.testclient import TestClient
-from docs_src.wsgi.tutorial001 import app
+from docs_src.wsgi.tutorial001_py39 import app
client = TestClient(app)