From 71c60bc5f56d0d18dfa88852722f41d2b4ae2224 Mon Sep 17 00:00:00 2001 From: Nils Lindemann Date: Sat, 30 Mar 2024 21:26:47 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=8C=90=20Add=20German=20translation=20for?= =?UTF-8?q?=20`docs/de/docs/tutorial/extra-models.md`=20(#10351)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/de/docs/tutorial/extra-models.md | 257 ++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 docs/de/docs/tutorial/extra-models.md diff --git a/docs/de/docs/tutorial/extra-models.md b/docs/de/docs/tutorial/extra-models.md new file mode 100644 index 000000000..cdf16c4ba --- /dev/null +++ b/docs/de/docs/tutorial/extra-models.md @@ -0,0 +1,257 @@ +# Extramodelle + +Fahren wir beim letzten Beispiel fort. Es gibt normalerweise mehrere zusammengehörende Modelle. + +Insbesondere Benutzermodelle, denn: + +* Das **hereinkommende Modell** sollte ein Passwort haben können. +* Das **herausgehende Modell** sollte kein Passwort haben. +* Das **Datenbankmodell** sollte wahrscheinlich ein gehashtes Passwort haben. + +!!! danger "Gefahr" + Speichern Sie niemals das Klartext-Passwort eines Benutzers. Speichern Sie immer den „sicheren Hash“, den Sie verifizieren können. + + Falls Ihnen das nichts sagt, in den [Sicherheits-Kapiteln](security/simple-oauth2.md#passwort-hashing){.internal-link target=_blank} werden Sie lernen, was ein „Passwort-Hash“ ist. + +## Mehrere Modelle + +Hier der generelle Weg, wie die Modelle mit ihren Passwort-Feldern aussehen könnten, und an welchen Orten sie verwendet werden würden. + +=== "Python 3.10+" + + ```Python hl_lines="7 9 14 20 22 27-28 31-33 38-39" + {!> ../../../docs_src/extra_models/tutorial001_py310.py!} + ``` + +=== "Python 3.8+" + + ```Python hl_lines="9 11 16 22 24 29-30 33-35 40-41" + {!> ../../../docs_src/extra_models/tutorial001.py!} + ``` + +!!! info + In Pydantic v1 hieß diese Methode `.dict()`, in Pydantic v2 wurde sie deprecated (aber immer noch unterstützt) und in `.model_dump()` umbenannt. + + Die Beispiele hier verwenden `.dict()` für die Kompatibilität mit Pydantic v1, Sie sollten jedoch stattdessen `.model_dump()` verwenden, wenn Sie Pydantic v2 verwenden können. + +### Über `**user_in.dict()` + +#### Pydantic's `.dict()` + +`user_in` ist ein Pydantic-Modell der Klasse `UserIn`. + +Pydantic-Modelle haben eine `.dict()`-Methode, die ein `dict` mit den Daten des Modells zurückgibt. + +Wenn wir also ein Pydantic-Objekt `user_in` erstellen, etwa so: + +```Python +user_in = UserIn(username="john", password="secret", email="john.doe@example.com") +``` + +und wir rufen seine `.dict()`-Methode auf: + +```Python +user_dict = user_in.dict() +``` + +dann haben wir jetzt in der Variable `user_dict` ein `dict` mit den gleichen Daten (es ist ein `dict` statt eines Pydantic-Modellobjekts). + +Wenn wir es ausgeben: + +```Python +print(user_dict) +``` + +bekommen wir ein Python-`dict`: + +```Python +{ + 'username': 'john', + 'password': 'secret', + 'email': 'john.doe@example.com', + 'full_name': None, +} +``` + +#### Ein `dict` entpacken + +Wenn wir ein `dict` wie `user_dict` nehmen, und es einer Funktion (oder Klassenmethode) mittels `**user_dict` übergeben, wird Python es „entpacken“. Es wird die Schlüssel und Werte von `user_dict` direkt als Schlüsselwort-Argumente übergeben. + +Wenn wir also das `user_dict` von oben nehmen und schreiben: + +```Python +UserInDB(**user_dict) +``` + +dann ist das ungefähr äquivalent zu: + +```Python +UserInDB( + username="john", + password="secret", + email="john.doe@example.com", + full_name=None, +) +``` + +Oder, präziser, `user_dict` wird direkt verwendet, welche Werte es auch immer haben mag: + +```Python +UserInDB( + username = user_dict["username"], + password = user_dict["password"], + email = user_dict["email"], + full_name = user_dict["full_name"], +) +``` + +#### Ein Pydantic-Modell aus den Inhalten eines anderen erstellen. + +Da wir in obigem Beispiel `user_dict` mittels `user_in.dict()` erzeugt haben, ist dieser Code: + +```Python +user_dict = user_in.dict() +UserInDB(**user_dict) +``` + +äquivalent zu: + +```Python +UserInDB(**user_in.dict()) +``` + +... weil `user_in.dict()` ein `dict` ist, und dann lassen wir Python es „entpacken“, indem wir es `UserInDB` übergeben, mit vorangestelltem `**`. + +Wir erhalten also ein Pydantic-Modell aus den Daten eines anderen Pydantic-Modells. + +#### Ein `dict` entpacken und zusätzliche Schlüsselwort-Argumente + +Und dann fügen wir ein noch weiteres Schlüsselwort-Argument hinzu, `hashed_password=hashed_password`: + +```Python +UserInDB(**user_in.dict(), hashed_password=hashed_password) +``` + +... was am Ende ergibt: + +```Python +UserInDB( + username = user_dict["username"], + password = user_dict["password"], + email = user_dict["email"], + full_name = user_dict["full_name"], + hashed_password = hashed_password, +) +``` + +!!! warning "Achtung" + Die Hilfsfunktionen `fake_password_hasher` und `fake_save_user` demonstrieren nur den möglichen Fluss der Daten und bieten natürlich keine echte Sicherheit. + +## Verdopplung vermeiden + +Reduzierung von Code-Verdoppelung ist eine der Kern-Ideen von **FastAPI**. + +Weil Verdoppelung von Code die Wahrscheinlichkeit von Fehlern, Sicherheitsproblemen, Desynchronisation (Code wird nur an einer Stelle verändert, aber nicht an einer anderen), usw. erhöht. + +Unsere Modelle teilen alle eine Menge der Daten und verdoppeln Attribut-Namen und -Typen. + +Das können wir besser machen. + +Wir deklarieren ein `UserBase`-Modell, das als Basis für unsere anderen Modelle dient. Dann können wir Unterklassen erstellen, die seine Attribute (Typdeklarationen, Validierungen, usw.) erben. + +Die ganze Datenkonvertierung, -validierung, -dokumentation, usw. wird immer noch wie gehabt funktionieren. + +Auf diese Weise beschreiben wir nur noch die Unterschiede zwischen den Modellen (mit Klartext-`password`, mit `hashed_password`, und ohne Passwort): + +=== "Python 3.10+" + + ```Python hl_lines="7 13-14 17-18 21-22" + {!> ../../../docs_src/extra_models/tutorial002_py310.py!} + ``` + +=== "Python 3.8+" + + ```Python hl_lines="9 15-16 19-20 23-24" + {!> ../../../docs_src/extra_models/tutorial002.py!} + ``` + +## `Union`, oder `anyOf` + +Sie können deklarieren, dass eine Response eine `Union` mehrerer Typen ist, sprich, einer dieser Typen. + +Das wird in OpenAPI mit `anyOf` angezeigt. + +Um das zu tun, verwenden Sie Pythons Standard-Typhinweis `typing.Union`: + +!!! note "Hinweis" + Listen Sie, wenn Sie eine `Union` definieren, denjenigen Typ zuerst, der am spezifischsten ist, gefolgt von den weniger spezifischen Typen. Im Beispiel oben, in `Union[PlaneItem, CarItem]` also den spezifischeren `PlaneItem` vor dem weniger spezifischen `CarItem`. + +=== "Python 3.10+" + + ```Python hl_lines="1 14-15 18-20 33" + {!> ../../../docs_src/extra_models/tutorial003_py310.py!} + ``` + +=== "Python 3.8+" + + ```Python hl_lines="1 14-15 18-20 33" + {!> ../../../docs_src/extra_models/tutorial003.py!} + ``` + +### `Union` in Python 3.10 + +In diesem Beispiel übergeben wir dem Argument `response_model` den Wert `Union[PlaneItem, CarItem]`. + +Da wir es als **Wert einem Argument überreichen**, statt es als **Typannotation** zu verwenden, müssen wir `Union` verwenden, selbst in Python 3.10. + +Wenn es eine Typannotation gewesen wäre, hätten wir auch den vertikalen Trennstrich verwenden können, wie in: + +```Python +some_variable: PlaneItem | CarItem +``` + +Aber wenn wir das in der Zuweisung `response_model=PlaneItem | CarItem` machen, erhalten wir eine Fehlermeldung, da Python versucht, eine **ungültige Operation** zwischen `PlaneItem` und `CarItem` durchzuführen, statt es als Typannotation zu interpretieren. + +## Listen von Modellen + +Genauso können Sie eine Response deklarieren, die eine Liste von Objekten ist. + +Verwenden Sie dafür Pythons Standard `typing.List` (oder nur `list` in Python 3.9 und darüber): + +=== "Python 3.9+" + + ```Python hl_lines="18" + {!> ../../../docs_src/extra_models/tutorial004_py39.py!} + ``` + +=== "Python 3.8+" + + ```Python hl_lines="1 20" + {!> ../../../docs_src/extra_models/tutorial004.py!} + ``` + +## Response mit beliebigem `dict` + +Sie könne auch eine Response deklarieren, die ein beliebiges `dict` zurückgibt, bei dem nur die Typen der Schlüssel und der Werte bekannt sind, ohne ein Pydantic-Modell zu verwenden. + +Das ist nützlich, wenn Sie die gültigen Feld-/Attribut-Namen von vorneherein nicht wissen (was für ein Pydantic-Modell notwendig ist). + +In diesem Fall können Sie `typing.Dict` verwenden (oder nur `dict` in Python 3.9 und darüber): + +=== "Python 3.9+" + + ```Python hl_lines="6" + {!> ../../../docs_src/extra_models/tutorial005_py39.py!} + ``` + +=== "Python 3.8+" + + ```Python hl_lines="1 8" + {!> ../../../docs_src/extra_models/tutorial005.py!} + ``` + +## Zusammenfassung + +Verwenden Sie gerne mehrere Pydantic-Modelle und vererben Sie je nach Bedarf. + +Sie brauchen kein einzelnes Datenmodell pro Einheit, wenn diese Einheit verschiedene Zustände annehmen kann. So wie unsere Benutzer-„Einheit“, welche einen Zustand mit `password`, einen mit `password_hash` und einen ohne Passwort hatte.