6 changed files with 693 additions and 0 deletions
@ -0,0 +1,7 @@ |
|||
# Testen einer Datenbank |
|||
|
|||
Sie können mehr über Datenbanken, SQL und SQLModel in den <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">SQLModel-Dokumentationen</a> lernen. 🤓 |
|||
|
|||
Es gibt ein Mini-<a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/" class="external-link" target="_blank">Tutorial zur Verwendung von SQLModel mit FastAPI</a>. ✨ |
|||
|
|||
Dieses Tutorial enthält einen Abschnitt über das <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/" class="external-link" target="_blank">Testen von SQL-Datenbanken</a>. 😎 |
@ -0,0 +1,76 @@ |
|||
# Cookie-Parameter-Modelle |
|||
|
|||
Wenn Sie eine Gruppe von **Cookies** haben, die zusammengehören, können Sie ein **Pydantic-Modell** erstellen, um sie zu deklarieren. 🍪 |
|||
|
|||
Dies ermöglicht es Ihnen, das **Modell** an **mehreren Stellen wiederzuverwenden** und auch Validierungen und Metadaten für alle Parameter auf einmal zu deklarieren. 😎 |
|||
|
|||
/// note | Hinweis |
|||
|
|||
Dies wird seit der FastAPI-Version `0.115.0` unterstützt. 🤓 |
|||
|
|||
/// |
|||
|
|||
/// tip | Tipp |
|||
|
|||
Diese Technik funktioniert genauso für `Query`, `Cookie` und `Header`. 😎 |
|||
|
|||
/// |
|||
|
|||
## Cookies mit einem Pydantic-Modell |
|||
|
|||
Deklarieren Sie die benötigten **Cookie**-Parameter in einem **Pydantic-Modell**, und deklarieren Sie dann den Parameter als `Cookie`: |
|||
|
|||
{* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *} |
|||
|
|||
**FastAPI** wird die Daten für **jedes Feld** aus den in der Anfrage erhaltenen **Cookies** extrahieren und Ihnen das von Ihnen definierte Pydantic-Modell bereitstellen. |
|||
|
|||
## Überprüfen Sie die Dokumentation |
|||
|
|||
Sie können die definierten Cookies in der Docs-Oberfläche unter `/docs` sehen: |
|||
|
|||
<div class="screenshot"> |
|||
<img src="/img/tutorial/cookie-param-models/image01.png"> |
|||
</div> |
|||
|
|||
/// info | Hinweis |
|||
|
|||
Berücksichtigen Sie, dass **Browser Cookies** auf spezielle Weise und im Hintergrund handhaben und sie **JavaScript** nicht leicht erlauben, diese zu berühren. |
|||
|
|||
Wenn Sie zur **API-Dokumentations-Oberfläche** unter `/docs` gehen, können Sie die **Dokumentation** für Cookies für Ihre *Pfadoperationen* sehen. |
|||
|
|||
Aber selbst wenn Sie die **Daten ausfüllen** und auf "Ausführen" klicken, werden die Cookies nicht gesendet, da die Docs-Oberfläche mit **JavaScript** arbeitet, und Sie erhalten eine **Fehlermeldung**, als ob Sie keine Werte eingegeben hätten. |
|||
|
|||
/// |
|||
|
|||
## Zusätzliche Cookies verbieten |
|||
|
|||
In einigen speziellen Anwendungsfällen (vermutlich nicht sehr häufig) möchten Sie möglicherweise die Cookies, die Sie erhalten möchten, **einschränken**. |
|||
|
|||
Ihre API hat jetzt die Macht, ihre eigene <abbr title="Das ist ein Scherz, nur für den Fall. Es hat nichts mit Cookie-Zustimmungen zu tun, aber es ist witzig, dass selbst die API die armen Cookies jetzt ablehnen kann. Haben Sie einen Cookie. 🍪">Cookie-Zustimmung</abbr> zu kontrollieren. 🤪🍪 |
|||
|
|||
Sie können die Modellkonfiguration von Pydantic verwenden, um alle `extra` Felder zu `verbieten`: |
|||
|
|||
{* ../../docs_src/cookie_param_models/tutorial002_an_py39.py hl[10] *} |
|||
|
|||
Wenn ein Client versucht, einige **zusätzliche Cookies** zu senden, erhält er eine **Error-Response**. |
|||
|
|||
Arme Cookie-Banner mit all ihrer Mühe, Ihre Zustimmung für die <abbr title="Das ist ein weiterer Scherz. Achtung: Nehmen Sie mich nicht zu ernst. Haben Sie einen Kaffee zu Ihrem Cookie. ☕">API zu bekommen, um sie abzulehnen</abbr>. 🍪 |
|||
|
|||
Wenn der Client beispielsweise versucht, ein `santa_tracker`-Cookie mit dem Wert `good-list-please` zu senden, erhält der Client eine **Error-Response**, die ihm mitteilt, dass das `santa_tracker`-<abbr title="Santa lehnt den Mangel an Cookies ab. 🎅 Okay, keine Cookie-Witze mehr.">Cookie nicht erlaubt</abbr> ist: |
|||
|
|||
```json |
|||
{ |
|||
"detail": [ |
|||
{ |
|||
"type": "extra_forbidden", |
|||
"loc": ["cookie", "santa_tracker"], |
|||
"msg": "Extra inputs are not permitted", |
|||
"input": "good-list-please", |
|||
} |
|||
] |
|||
} |
|||
``` |
|||
|
|||
## Zusammenfassung |
|||
|
|||
Sie können **Pydantic-Modelle** verwenden, um <abbr title="Nehmen Sie sich einen letzten Cookie, bevor Sie gehen. 🍪">**Cookies**</abbr> in **FastAPI** zu deklarieren. 😎 |
@ -0,0 +1,113 @@ |
|||
# Debugging |
|||
|
|||
Sie können den Debugger in Ihrem Editor verbinden, zum Beispiel mit Visual Studio Code oder PyCharm. |
|||
|
|||
## Aufruf von `uvicorn` |
|||
|
|||
Importieren und führen Sie `uvicorn` direkt in Ihrer FastAPI-Anwendung aus: |
|||
|
|||
{* ../../docs_src/debugging/tutorial001.py hl[1,15] *} |
|||
|
|||
### Über `__name__ == "__main__"` |
|||
|
|||
Der Hauptzweck von `__name__ == "__main__"` ist, dass bestimmter Code ausgeführt wird, wenn Ihre Datei mit: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ python myapp.py |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
aufgerufen wird, aber nicht ausgeführt wird, wenn eine andere Datei sie importiert, wie in: |
|||
|
|||
```Python |
|||
from myapp import app |
|||
``` |
|||
|
|||
#### Mehr Details |
|||
|
|||
Angenommen, Ihre Datei heißt `myapp.py`. |
|||
|
|||
Wenn Sie sie mit: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ python myapp.py |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
ausführen, hat die interne Variable `__name__` in Ihrer Datei, die automatisch von Python erstellt wird, den Wert der Zeichenkette `"__main__"`. |
|||
|
|||
Also wird der Abschnitt: |
|||
|
|||
```Python |
|||
uvicorn.run(app, host="0.0.0.0", port=8000) |
|||
``` |
|||
|
|||
ausgeführt. |
|||
|
|||
--- |
|||
|
|||
Das wird nicht passieren, wenn Sie dieses Modul (Datei) importieren. |
|||
|
|||
Wenn Sie also eine andere Datei `importer.py` mit folgendem Inhalt haben: |
|||
|
|||
```Python |
|||
from myapp import app |
|||
|
|||
# Weiterer Code |
|||
``` |
|||
|
|||
wird in diesem Fall die automatisch erstellte Variable innerhalb von `myapp.py` nicht den Wert `"__main__"` für die Variable `__name__` haben. |
|||
|
|||
Daher wird die Zeile: |
|||
|
|||
```Python |
|||
uvicorn.run(app, host="0.0.0.0", port=8000) |
|||
``` |
|||
|
|||
nicht ausgeführt. |
|||
|
|||
/// info | Hinweis |
|||
|
|||
Für weitere Informationen schauen Sie in die <a href="https://docs.python.org/3/library/__main__.html" class="external-link" target="_blank">offiziellen Python-Dokumentationen</a>. |
|||
|
|||
/// |
|||
|
|||
## Führen Sie Ihren Code mit Ihrem Debugger aus |
|||
|
|||
Da Sie den Uvicorn-Server direkt aus Ihrem Code heraus ausführen, können Sie Ihr Python-Programm (Ihre FastAPI-Anwendung) direkt aus dem Debugger aufrufen. |
|||
|
|||
--- |
|||
|
|||
Zum Beispiel können Sie in Visual Studio Code: |
|||
|
|||
* Zum "Debug"-Panel gehen. |
|||
* "Konfiguration hinzufügen..." wählen. |
|||
* "Python" auswählen. |
|||
* Den Debugger mit der Option "`Python: Current File (Integrated Terminal)`" ausführen. |
|||
|
|||
Damit wird dann der Server mit Ihrem **FastAPI**-Code gestartet, an Ihren Breakpoints angehalten usw. |
|||
|
|||
So könnte es aussehen: |
|||
|
|||
<img src="/img/tutorial/debugging/image01.png"> |
|||
|
|||
--- |
|||
|
|||
Wenn Sie Pycharm verwenden, können Sie: |
|||
|
|||
* Das "Run"-Menü öffnen. |
|||
* Die Option "Debug..." auswählen. |
|||
* Es erscheint ein Kontextmenü. |
|||
* Die Datei zum Debuggen auswählen (in diesem Fall `main.py`). |
|||
|
|||
Damit wird dann der Server mit Ihrem **FastAPI**-Code gestartet, an Ihren Breakpoints angehalten usw. |
|||
|
|||
So könnte es aussehen: |
|||
|
|||
<img src="/img/tutorial/debugging/image02.png"> |
@ -0,0 +1,72 @@ |
|||
# Header-Parameter-Modelle |
|||
|
|||
Wenn Sie eine Gruppe von verwandten **Header-Parametern** haben, können Sie ein **Pydantic-Modell** erstellen, um diese zu deklarieren. |
|||
|
|||
Dies ermöglicht es Ihnen, das **Modell wiederzuverwenden** in **mehreren Stellen** und auch Validierungen und Metadaten für alle Parameter auf einmal zu deklarieren. 😎 |
|||
|
|||
/// note | Hinweis |
|||
|
|||
Dies wird seit FastAPI-Version `0.115.0` unterstützt. 🤓 |
|||
|
|||
/// |
|||
|
|||
## Header-Parameter mit einem Pydantic-Modell |
|||
|
|||
Deklarieren Sie die **Header-Parameter**, die Sie benötigen, in einem **Pydantic-Modell** und dann den Parameter als `Header`: |
|||
|
|||
{* ../../docs_src/header_param_models/tutorial001_an_py310.py hl[9:14,18] *} |
|||
|
|||
**FastAPI** wird die Daten für **jedes Feld** aus den **Headers** in der Anfrage **extrahieren** und Ihnen das definierte Pydantic-Modell übergeben. |
|||
|
|||
## Überprüfen Sie die Dokumentation |
|||
|
|||
Sie können die erforderlichen Headers in der Dokumentationsoberfläche unter `/docs` sehen: |
|||
|
|||
<div class="screenshot"> |
|||
<img src="/img/tutorial/header-param-models/image01.png"> |
|||
</div> |
|||
|
|||
## Zusätzliche Headers verbieten |
|||
|
|||
In einigen speziellen Anwendungsfällen (wahrscheinlich nicht sehr häufig) möchten Sie möglicherweise die **Headers einschränken**, die Sie empfangen möchten. |
|||
|
|||
Sie können die Modellkonfiguration von Pydantic verwenden, um `zusätzliche` Felder zu `verbieten`: |
|||
|
|||
{* ../../docs_src/header_param_models/tutorial002_an_py310.py hl[10] *} |
|||
|
|||
Wenn ein Client versucht, einige **zusätzliche Headers** zu senden, erhalten sie eine **Error-Response**. |
|||
|
|||
Zum Beispiel, wenn der Client versucht, einen `tool`-Header mit dem Wert `plumbus` zu senden, erhalten sie eine **Error-Response**, die ihnen mitteilt, dass der Header-Parameter `tool` nicht erlaubt ist: |
|||
|
|||
```json |
|||
{ |
|||
"detail": [ |
|||
{ |
|||
"type": "extra_forbidden", |
|||
"loc": ["header", "tool"], |
|||
"msg": "Extra inputs are not permitted", |
|||
"input": "plumbus", |
|||
} |
|||
] |
|||
} |
|||
``` |
|||
|
|||
## Automatische Konvertierung von Unterstrichen deaktivieren |
|||
|
|||
Genauso wie bei regulären Header-Parametern, werden bei der Verwendung von Unterstrich-Zeichen in den Parameternamen diese **automatisch in Bindestriche umgewandelt**. |
|||
|
|||
Zum Beispiel, wenn Sie einen Header-Parameter `save_data` im Code haben, wird der erwartete HTTP-Header `save-data` sein, und er wird auch so in der Dokumentation erscheinen. |
|||
|
|||
Wenn Sie aus irgendeinem Grund diese automatische Konvertierung deaktivieren müssen, können Sie dies auch für Pydantic-Modelle für Header-Parameter tun. |
|||
|
|||
{* ../../docs_src/header_param_models/tutorial003_an_py310.py hl[19] *} |
|||
|
|||
/// warning | Achtung |
|||
|
|||
Bevor Sie `convert_underscores` auf `False` setzen, bedenken Sie, dass einige HTTP-Proxies und Server die Verwendung von Headers mit Unterstrichen nicht zulassen. |
|||
|
|||
/// |
|||
|
|||
## Zusammenfassung |
|||
|
|||
Sie können **Pydantic-Modelle** verwenden, um **Headers** in **FastAPI** zu deklarieren. 😎 |
@ -0,0 +1,68 @@ |
|||
# Query-Parameter-Modelle |
|||
|
|||
Wenn Sie eine Gruppe von **Query-Parametern** haben, die in Zusammenhang stehen, können Sie ein **Pydantic-Modell** erstellen, um sie zu deklarieren. |
|||
|
|||
Dadurch können Sie das **Modell** an **mehreren Stellen wiederverwenden** und auch Validierungen und Metadaten für alle Parameter auf einmal deklarieren. 😎 |
|||
|
|||
/// note | Hinweis |
|||
|
|||
Dies wird seit FastAPI-Version `0.115.0` unterstützt. 🤓 |
|||
|
|||
/// |
|||
|
|||
## Query-Parameter mit einem Pydantic-Modell |
|||
|
|||
Deklarieren Sie die benötigten **Query-Parameter** in einem **Pydantic-Modell** und deklarieren Sie dann den Parameter als `Query`: |
|||
|
|||
{* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *} |
|||
|
|||
**FastAPI** wird die Daten für **jedes Feld** aus den **Query-Parametern** der Anfrage **extrahieren** und Ihnen das von Ihnen definierte Pydantic-Modell übergeben. |
|||
|
|||
## Überprüfen Sie die Dokumentation |
|||
|
|||
Sie können die Query-Parameter in der Dokumentationsoberfläche unter `/docs` sehen: |
|||
|
|||
<div class="screenshot"> |
|||
<img src="/img/tutorial/query-param-models/image01.png"> |
|||
</div> |
|||
|
|||
## Zusätzliche Query-Parameter verbieten |
|||
|
|||
In einigen speziellen Anwendungsfällen (wahrscheinlich nicht sehr häufig) möchten Sie möglicherweise die Query-Parameter, die Sie empfangen möchten, **einschränken**. |
|||
|
|||
Sie können die Modellkonfiguration von Pydantic verwenden, um `extra` Felder zu `verbieten`: |
|||
|
|||
{* ../../docs_src/query_param_models/tutorial002_an_py310.py hl[10] *} |
|||
|
|||
Wenn ein Client versucht, einige **zusätzliche** Daten in den **Query-Parametern** zu senden, erhält er eine **Error-Response**. |
|||
|
|||
Wenn der Client beispielsweise versucht, einen `tool` Query-Parameter mit einem Wert von `plumbus` zu senden, wie: |
|||
|
|||
```http |
|||
https://example.com/items/?limit=10&tool=plumbus |
|||
``` |
|||
|
|||
wird er eine **Error-Response** erhalten, die ihm mitteilt, dass der Query-Parameter `tool` nicht erlaubt ist: |
|||
|
|||
```json |
|||
{ |
|||
"detail": [ |
|||
{ |
|||
"type": "extra_forbidden", |
|||
"loc": ["query", "tool"], |
|||
"msg": "Extra inputs are not permitted", |
|||
"input": "plumbus" |
|||
} |
|||
] |
|||
} |
|||
``` |
|||
|
|||
## Zusammenfassung |
|||
|
|||
Sie können **Pydantic-Modelle** verwenden, um **Query-Parameter** in **FastAPI** zu deklarieren. 😎 |
|||
|
|||
/// tip | Tipp |
|||
|
|||
Spoiler-Alarm: Sie können auch Pydantic-Modelle verwenden, um Cookies und Header zu deklarieren, aber darüber werden Sie später im Tutorial lesen. 🤫 |
|||
|
|||
/// |
@ -0,0 +1,357 @@ |
|||
# SQL (Relationale) Datenbanken |
|||
|
|||
**FastAPI** erfordert nicht, dass Sie eine SQL (relationale) Datenbank verwenden. Aber Sie können **jede beliebige Datenbank** verwenden, die Sie möchten. |
|||
|
|||
Hier werden wir ein Beispiel mit <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">SQLModel</a> sehen. |
|||
|
|||
**SQLModel** basiert auf <a href="https://www.sqlalchemy.org/" class="external-link" target="_blank">SQLAlchemy</a> und Pydantic. Es wurde vom selben Autor wie **FastAPI** entwickelt, um die perfekte Ergänzung für FastAPI-Anwendungen zu sein, die **SQL-Datenbanken** verwenden müssen. |
|||
|
|||
/// tip | Tipp |
|||
|
|||
Sie könnten jede andere SQL- oder NoSQL-Datenbankbibliothek verwenden, die Sie möchten (in einigen Fällen als <abbr title="Object Relational Mapper, ein ausgefallener Begriff für eine Bibliothek, bei der einige Klassen SQL-Tabellen darstellen und Instanzen Zeilen in diesen Tabellen repräsentieren">„ORMs“</abbr> bezeichnet), FastAPI zwingt Sie nicht, irgendetwas zu verwenden. 😎 |
|||
|
|||
/// |
|||
|
|||
Da SQLModel auf SQLAlchemy basiert, können Sie problemlos **jede von SQLAlchemy unterstützte Datenbank** verwenden (was auch bedeutet, dass sie von SQLModel unterstützt werden), wie: |
|||
|
|||
* PostgreSQL |
|||
* MySQL |
|||
* SQLite |
|||
* Oracle |
|||
* Microsoft SQL Server usw. |
|||
|
|||
In diesem Beispiel verwenden wir **SQLite**, da es eine einzige Datei verwendet und Python integrierte Unterstützung bietet. Sie können dieses Beispiel kopieren und direkt so ausführen. |
|||
|
|||
Später, für Ihre Produktionsanwendung, möchten Sie möglicherweise einen Datenbankserver wie **PostgreSQL** verwenden. |
|||
|
|||
/// tip | Tipp |
|||
|
|||
Es gibt einen offiziellen Projektgenerator mit **FastAPI** und **PostgreSQL**, einschließlich eines Frontends und weiterer Tools: <a href="https://github.com/fastapi/full-stack-fastapi-template" class="external-link" target="_blank">https://github.com/fastapi/full-stack-fastapi-template</a> |
|||
|
|||
/// |
|||
|
|||
Dies ist ein sehr einfaches und kurzes Tutorial. Wenn Sie mehr über Datenbanken im Allgemeinen, über SQL oder fortgeschrittenere Funktionen erfahren möchten, gehen Sie zu den <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">SQLModel-Dokumentationen</a>. |
|||
|
|||
## Installieren Sie `SQLModel` |
|||
|
|||
Stellen Sie zunächst sicher, dass Sie Ihre [virtuelle Umgebung](../virtual-environments.md){.internal-link target=_blank} erstellen, sie aktivieren und dann `sqlmodel` installieren: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ pip install sqlmodel |
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
## Erstellen Sie die Anwendung mit einem einzigen Modell |
|||
|
|||
Wir erstellen zuerst die einfachste erste Version der Anwendung mit einem einzigen **SQLModel**-Modell. |
|||
|
|||
Später werden wir sie verbessern, indem wir die Sicherheit und die Vielseitigkeit mit **mehreren Modellen** weiter unten erhöhen. 🤓 |
|||
|
|||
### Modelle erstellen |
|||
|
|||
Importieren Sie `SQLModel` und erstellen Sie ein Datenbankmodell: |
|||
|
|||
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[1:11] hl[7:11] *} |
|||
|
|||
Die `Hero`-Klasse ist einem Pydantic-Modell sehr ähnlich (tatsächlich ist sie darunter tatsächlich *ein Pydantic-Modell*). |
|||
|
|||
Es gibt einige Unterschiede: |
|||
|
|||
* `table=True` sagt SQLModel, dass dies ein *Tabellenmodell* ist, es soll eine **Tabelle** in der SQL-Datenbank darstellen, es ist nicht nur ein *Datenmodell* (wie es jede andere reguläre Pydantic-Klasse wäre). |
|||
|
|||
* `Field(primary_key=True)` sagt SQLModel, dass die `id` der **Primärschlüssel** in der SQL-Datenbank ist (Sie können mehr über SQL-Primärschlüssel in den SQLModel-Dokumentationen erfahren). |
|||
|
|||
Durch das Festlegen des Typs als `int | None` wird SQLModel wissen, dass diese Spalte ein `INTEGER` in der SQL-Datenbank sein sollte und dass sie `NULLABLE` sein sollte. |
|||
|
|||
* `Field(index=True)` sagt SQLModel, dass es einen **SQL-Index** für diese Spalte erstellen soll, der schnellere Lookups in der Datenbank ermöglicht, wenn Daten durch diese Spalte gefiltert gelesen werden. |
|||
|
|||
SQLModel wird verstehen, dass etwas, das als `str` deklariert ist, eine SQL-Spalte des Typs `TEXT` (oder `VARCHAR`, abhängig von der Datenbank) sein wird. |
|||
|
|||
### Erstellen Sie einen Engine |
|||
|
|||
Eine SQLModel `engine` (darunter ist es tatsächlich eine SQLAlchemy `engine`) ist das, was die **Verbindungen** zur Datenbank hält. |
|||
|
|||
Sie würden **ein einzelnes `engine`-Objekt** für Ihren gesamten Code verwenden, um sich mit demselben Datenbank zu verbinden. |
|||
|
|||
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[14:18] hl[14:15,17:18] *} |
|||
|
|||
Die Verwendung von `check_same_thread=False` erlaubt FastAPI, dieselbe SQLite-Datenbank in verschiedenen Threads zu verwenden. Dies ist notwendig, da **eine einzige Anfrage** **mehr als einen Thread** verwenden könnte (zum Beispiel in Abhängigkeiten). |
|||
|
|||
Keine Sorge, mit der Art und Weise, wie der Code strukturiert ist, werden wir sicherstellen, dass wir **eine einzige SQLModel-Session pro Anfrage** später verwenden, dies ist tatsächlich das, was `check_same_thread` zu erreichen versucht. |
|||
|
|||
### Erstellen Sie die Tabellen |
|||
|
|||
Dann fügen wir eine Funktion hinzu, die `SQLModel.metadata.create_all(engine)` verwendet, um die **Tabellen für alle Tabellenmodelle** zu erstellen. |
|||
|
|||
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[21:22] hl[21:22] *} |
|||
|
|||
### Erstellen Sie eine Session-Abhängigkeit |
|||
|
|||
Eine **`Session`** speichert die **Objekte im Speicher** und verfolgt alle notwendigen Änderungen in den Daten, dann **verwendet sie die `engine`**, um mit der Datenbank zu kommunizieren. |
|||
|
|||
Wir werden eine FastAPI **Abhängigkeit** mit `yield` erstellen, die eine neue `Session` für jede Anfrage bereitstellt. Dies ist das, was sicherstellt, dass wir eine einzige Session pro Anfrage verwenden. 🤓 |
|||
|
|||
Dann erstellen wir eine `Annotated` Abhängigkeit `SessionDep`, um den Rest des Codes zu vereinfachen, der diese Abhängigkeit nutzen wird. |
|||
|
|||
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[25:30] hl[25:27,30] *} |
|||
|
|||
### Erstellen Sie die Datenbanktabellen beim Start |
|||
|
|||
Wir werden die Datenbanktabellen erstellen, wenn die Anwendung startet. |
|||
|
|||
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[32:37] hl[35:37] *} |
|||
|
|||
Hier erstellen wir die Tabellen bei einem Anwendungsstart-Event. |
|||
|
|||
Für die Produktion würden Sie wahrscheinlich ein Migrationsskript verwenden, das ausgeführt wird, bevor Sie Ihre App starten. 🤓 |
|||
|
|||
/// tip | Tipp |
|||
|
|||
SQLModel wird Migrationstools mit Alembic bereitstellen, aber vorerst können Sie <a href="https://alembic.sqlalchemy.org/en/latest/" class="external-link" target="_blank">Alembic</a> direkt verwenden. |
|||
|
|||
/// |
|||
|
|||
### Erstellen Sie einen Helden |
|||
|
|||
Da jedes SQLModel-Modell auch ein Pydantic-Modell ist, können Sie es in denselben **Typ-Annotationen** verwenden, die Sie für Pydantic-Modelle verwenden könnten. |
|||
|
|||
Wenn Sie beispielsweise einen Parameter vom Typ `Hero` deklarieren, wird er aus dem **JSON-Body** gelesen. |
|||
|
|||
Auf die gleiche Weise können Sie ihn als Rückgabewert der Funktion deklarieren, und dann wird die Form der Daten in der automatischen API-Dokumentations-UI angezeigt. |
|||
|
|||
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *} |
|||
|
|||
Hier verwenden wir die `SessionDep`-Abhängigkeit (eine `Session`), um den neuen `Hero` zum `Session`-Objekt hinzuzufügen, die Änderungen in der Datenbank zu übermitteln, die Daten im `hero` zu aktualisieren und dann zurückzugeben. |
|||
|
|||
### Helden lesen |
|||
|
|||
Wir können **Helden** aus der Datenbank mit `select()` lesen. Wir können ein `limit` und `offset` einfügen, um die Ergebnisse zu paginieren. |
|||
|
|||
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[48:55] hl[51:52,54] *} |
|||
|
|||
### Einen Helden lesen |
|||
|
|||
Wir können einen einzelnen **Helden** lesen. |
|||
|
|||
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[58:63] hl[60] *} |
|||
|
|||
### Einen Helden löschen |
|||
|
|||
Wir können auch einen **Helden** löschen. |
|||
|
|||
{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[66:73] hl[71] *} |
|||
|
|||
### Führen Sie die App aus |
|||
|
|||
Sie können die App ausführen: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ fastapi dev main.py |
|||
|
|||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Gehen Sie dann zur `/docs` UI, Sie werden sehen, dass **FastAPI** diese **Modelle** verwendet, um die API zu **dokumentieren**, und es wird sie verwenden, um die Daten zu **serialisieren** und zu **validieren**. |
|||
|
|||
<div class="screenshot"> |
|||
<img src="/img/tutorial/sql-databases/image01.png"> |
|||
</div> |
|||
|
|||
## Aktualisieren Sie die App mit mehreren Modellen |
|||
|
|||
Jetzt lassen Sie uns diese App ein wenig **umstellen**, um die **Sicherheit** und **Vielseitigkeit** zu erhöhen. |
|||
|
|||
Wenn Sie die vorherige App überprüfen, können Sie in der UI sehen, dass sie dem Client erlaubt, die `id` des zu erstellenden `Hero` zu entscheiden. 😱 |
|||
|
|||
Das sollten wir nicht zulassen, sie könnten eine `id` überschreiben, die wir bereits in der DB zugewiesen haben. Das Festlegen der `id` sollte vom **Backend** oder der **Datenbank**, **nicht vom Client** erfolgen. |
|||
|
|||
Darüber hinaus erstellen wir einen `secret_name` für den Helden, aber bisher geben wir ihn überall zurück, das ist nicht sehr **geheim**... 😅 |
|||
|
|||
Wir werden diese Dinge beheben, indem wir ein paar **zusätzliche Modelle** hinzufügen. Hier wird SQLModel glänzen. ✨ |
|||
|
|||
### Mehrere Modelle erstellen |
|||
|
|||
In **SQLModel** ist jede Modellklasse, die `table=True` hat, ein **Tabellenmodell**. |
|||
|
|||
Und jede Modellklasse, die `table=True` nicht hat, ist ein **Datenmodell**, diese sind tatsächlich nur Pydantic-Modelle (mit ein paar kleinen zusätzlichen Funktionen). 🤓 |
|||
|
|||
Mit SQLModel können wir **Vererbung** verwenden, um **doppelte Felder** in allen Fällen zu **vermeiden**. |
|||
|
|||
#### `HeroBase` - die Basisklasse |
|||
|
|||
Beginnen wir mit einem `HeroBase`-Modell, das alle **Felder, die von allen Modellen gemeinsam genutzt werden**, enthält: |
|||
|
|||
* `name` |
|||
* `age` |
|||
|
|||
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:9] hl[7:9] *} |
|||
|
|||
#### `Hero` - das *Tabellenmodell* |
|||
|
|||
Dann erstellen wir `Hero`, das eigentliche *Tabellenmodell*, mit den **zusätzlichen Feldern**, die nicht immer in den anderen Modellen sind: |
|||
|
|||
* `id` |
|||
* `secret_name` |
|||
|
|||
Da `Hero` von `HeroBase` erbt, hat es **auch** die **Felder**, die in `HeroBase` deklariert sind, sodass alle Felder für `Hero` sind: |
|||
|
|||
* `id` |
|||
* `name` |
|||
* `age` |
|||
* `secret_name` |
|||
|
|||
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:14] hl[12:14] *} |
|||
|
|||
#### `HeroPublic` - das öffentliche *Datenmodell* |
|||
|
|||
Als nächstes erstellen wir ein `HeroPublic`-Modell, dies ist das, das **an die API-Clients zurückgegeben** wird. |
|||
|
|||
Es hat dieselben Felder wie `HeroBase`, also wird es `secret_name` nicht enthalten. |
|||
|
|||
Endlich ist die Identität unserer Helden geschützt! 🥷 |
|||
|
|||
Es erklärt auch `id: int` neu. Indem wir dies tun, schließen wir einen **Vertrag** mit den API-Clients ab, sodass sie immer erwarten können, dass die `id` vorhanden ist und ein `int` ist (sie wird niemals `None` sein). |
|||
|
|||
/// tip | Tipp |
|||
|
|||
Dass das Rückgabemodell sicherstellt, dass ein Wert immer verfügbar ist und immer `int` ist (nicht `None`), ist sehr nützlich für die API-Clients, sie können viel einfacheren Code schreiben, wenn sie diese Sicherheit haben. |
|||
|
|||
Auch **automatisch generierte Clients** werden einfachere Schnittstellen haben, sodass die Entwickler, die mit Ihrer API kommunizieren, eine viel bessere Zeit haben können, mit Ihrer API zu arbeiten. 😎 |
|||
|
|||
/// |
|||
|
|||
Alle Felder in `HeroPublic` sind die gleichen wie in `HeroBase`, mit `id` als `int` (nicht `None`): |
|||
|
|||
* `id` |
|||
* `name` |
|||
* `age` |
|||
|
|||
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *} |
|||
|
|||
#### `HeroCreate` - das *Datenmodell* um einen Helden zu erstellen |
|||
|
|||
Jetzt erstellen wir ein `HeroCreate`-Modell, das die Daten von den Clients **validieren** wird. |
|||
|
|||
Es hat die gleichen Felder wie `HeroBase`, und es hat auch `secret_name`. |
|||
|
|||
Jetzt, wenn die Clients **einen neuen Helden erstellen**, senden sie den `secret_name`, er wird in der Datenbank gespeichert, aber diese geheimen Namen werden in der API nicht an die Clients zurückgegeben. |
|||
|
|||
/// tip | Tipp |
|||
|
|||
So würden Sie **Passwörter** behandeln. Sie empfangen sie, aber geben sie nicht in der API zurück. |
|||
|
|||
Sie würden auch **die Werte der Passwörter hashieren**, bevor Sie sie speichern, **niemals im Klartext speichern**. |
|||
|
|||
/// |
|||
|
|||
Die Felder von `HeroCreate` sind: |
|||
|
|||
* `name` |
|||
* `age` |
|||
* `secret_name` |
|||
|
|||
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:22] hl[21:22] *} |
|||
|
|||
#### `HeroUpdate` - das *Datenmodell* um einen Helden zu aktualisieren |
|||
|
|||
Wir hatten keine Möglichkeit, **einen Helden zu aktualisieren** in der vorherigen Version der App, aber jetzt mit **mehreren Modellen**, können wir es. 🎉 |
|||
|
|||
Das `HeroUpdate` *Datenmodell* ist etwas Besonderes, es hat **alle gleichen Felder**, die benötigt werden, um einen neuen Helden zu erstellen, aber alle Felder sind **optional** (sie haben alle einen Defaultwert). Auf diese Weise, wenn Sie einen Helden aktualisieren, können Sie nur die Felder senden, die Sie aktualisieren möchten. |
|||
|
|||
Da sich alle **Felder wirklich ändern** (der Typ enthält nun `None` und sie haben jetzt einen Defaultwert von `None`), müssen wir sie **neu deklarieren**. |
|||
|
|||
Wir müssen nicht wirklich von `HeroBase` erben, da wir alle Felder neu deklarieren. Ich lasse es einfach nur aus Gründen der Konsistenz erben, aber das ist nicht notwendig. Es ist mehr eine Frage des persönlichen Geschmacks. 🤷 |
|||
|
|||
Die Felder von `HeroUpdate` sind: |
|||
|
|||
* `name` |
|||
* `age` |
|||
* `secret_name` |
|||
|
|||
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:28] hl[25:28] *} |
|||
|
|||
### Erstellen mit `HeroCreate` und Rückgabe eines `HeroPublic` |
|||
|
|||
Jetzt, da wir **mehrere Modelle** haben, können wir die Teile der App aktualisieren, die sie verwenden. |
|||
|
|||
Wir empfangen in der Anfrage ein `HeroCreate` *Datenmodell*, und daraus erstellen wir ein `Hero` *Tabellenmodell*. |
|||
|
|||
Dieses neue *Tabellenmodell* `Hero` wird die Felder haben, die vom Client gesendet wurden, und wird auch eine `id` haben, die von der Datenbank generiert wird. |
|||
|
|||
Dann geben wir das gleiche *Tabellenmodell* `Hero` wie aus der Funktion aus zurück. Aber da wir das `response_model` mit dem `HeroPublic` *Datenmodell* deklarieren, wird **FastAPI** `HeroPublic` verwenden, um die Daten zu validieren und zu serialisieren. |
|||
|
|||
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[56:62] hl[56:58] *} |
|||
|
|||
/// tip | Tipp |
|||
|
|||
Jetzt verwenden wir `response_model=HeroPublic` anstelle der **Rückgabetyp-Annotation** `-> HeroPublic`, weil der Wert, den wir zurückgeben, tatsächlich *kein* `HeroPublic` ist. |
|||
|
|||
Hätten wir `-> HeroPublic` deklariert, würde Ihr Editor und Linter (berechtigterweise) meckern, dass Sie ein `Hero` statt eines `HeroPublic` zurückgeben. |
|||
|
|||
Durch die Deklaration in `response_model` sagen wir **FastAPI**, dass es sein Ding machen soll, ohne die Typ-Annotationen und die Hilfe Ihres Editors und anderer Tools zu stören. |
|||
|
|||
/// |
|||
|
|||
### Helden mit `HeroPublic` lesen |
|||
|
|||
Wir können dasselbe wie zuvor tun, um **Helden zu lesen**, erneut verwenden wir `response_model=list[HeroPublic]`, um sicherzustellen, dass die Daten korrekt validiert und serialisiert werden. |
|||
|
|||
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[65:72] hl[65] *} |
|||
|
|||
### Einen Helden mit `HeroPublic` lesen |
|||
|
|||
Wir können einen einzelnen **Helden** lesen: |
|||
|
|||
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[75:80] hl[77] *} |
|||
|
|||
### Update einen Helden mit `HeroUpdate` |
|||
|
|||
Wir können einen **Helden aktualisieren**. Dafür verwenden wir eine HTTP `PATCH`-Operation. |
|||
|
|||
Und im Code erhalten wir ein `dict` mit allen Daten, die vom Client gesendet wurden, **nur die Daten, die vom Client gesendet wurden**, ohne Werte, die nur für die Defaultwerte vorhanden wären. Dazu verwenden wir `exclude_unset=True`. Dies ist der Haupttrick. 🪄 |
|||
|
|||
Dann verwenden wir `hero_db.sqlmodel_update(hero_data)`, um das `hero_db` mit den Daten von `hero_data` zu aktualisieren. |
|||
|
|||
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[83:93] hl[83:84,88:89] *} |
|||
|
|||
### Einen Helden erneut löschen |
|||
|
|||
Das **Löschen** eines Helden bleibt weitgehend gleich. |
|||
|
|||
Wir werden das Verlangen, alles in diesem Fall umzugestalten, nicht befriedigen. 😅 |
|||
|
|||
{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[96:103] hl[101] *} |
|||
|
|||
### Führen Sie die App erneut aus |
|||
|
|||
Sie können die App noch einmal ausführen: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ fastapi dev main.py |
|||
|
|||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
Wenn Sie zur `/docs` API UI gehen, werden Sie sehen, dass diese jetzt aktualisiert wurde. Sie wird nicht erwarten, die `id` vom Client zu erhalten, wenn sie einen Helden erstellt, usw. |
|||
|
|||
<div class="screenshot"> |
|||
<img src="/img/tutorial/sql-databases/image02.png"> |
|||
</div> |
|||
|
|||
## Zusammenfassung |
|||
|
|||
Sie können <a href="https://sqlmodel.tiangolo.com/" class="external-link" target="_blank">**SQLModel**</a> verwenden, um mit einer SQL-Datenbank zu interagieren und den Code mit *Datenmodellen* und *Tabellenmodellen* zu vereinfachen. |
|||
|
|||
Sie können viel mehr in den **SQLModel**-Dokumentationen lernen, es gibt ein längeres Mini <a href="https://sqlmodel.tiangolo.com/tutorial/fastapi/" class="external-link" target="_blank">Tutorial über die Verwendung von SQLModel mit **FastAPI**</a>. 🚀 |
Loading…
Reference in new issue