]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
:memo: Add docs for extra models
authorSebastián Ramírez <tiangolo@gmail.com>
Fri, 14 Dec 2018 17:58:01 +0000 (21:58 +0400)
committerSebastián Ramírez <tiangolo@gmail.com>
Fri, 14 Dec 2018 17:58:01 +0000 (21:58 +0400)
docs/tutorial/extra-models.md [new file with mode: 0644]
docs/tutorial/src/extra-models/tutorial001.py [new file with mode: 0644]
docs/tutorial/src/extra-models/tutorial002.py [new file with mode: 0644]
mkdocs.yml

diff --git a/docs/tutorial/extra-models.md b/docs/tutorial/extra-models.md
new file mode 100644 (file)
index 0000000..1e24c74
--- /dev/null
@@ -0,0 +1,45 @@
+Continuing with the previous example, it will be common to have more than one related model.
+
+This is especially the case for user models, because:
+
+* The **input model** needs to be able to have a password
+* The **output model** should do not have a password
+* The **database model** would probably need to have a hashed password
+
+!!! danger
+    Never store user's plaintext passwords. Always store a secure hash that you can then verify.
+
+## Multiple models
+
+Here's a general idea of how the models could look like with their password fields and the places where they are used:
+
+```Python hl_lines="8 10 15 21 23 32 34 39 40"
+{!./tutorial/src/extra-models/tutorial001.py!}
+```
+
+!!! warning
+    The supporting additional functions are just to demo a possible flow of the data, but they of course are not providing any real security.
+
+## Reduce duplication
+
+Reducing code duplication is one of the core ideas in **FastAPI**.
+
+As code duplication increments the chances of bugs, security issues, code desynchronization issues (when you update in one place but not in the others), etc.
+
+And these models are all sharing a lot of the data and duplicating attribute names and types.
+
+We could do better.
+
+We can declare a `Userbase` model that serves as a base for our other models. And then we can make subclasses of that model that inherit its attributes (type declarations, validation, etc).
+
+All the data conversion, validation, documentation, etc. will still work as normally.
+
+That way, we can declare just the differences between the models (with plaintext `password`, with `hashed_password` and without password):
+
+```Python hl_lines="8 14 15 18 19 22 23"
+{!./tutorial/src/extra-models/tutorial002.py!}
+```
+
+## Recap
+
+Use multiple Pydantic models and inherit freely for each case. You don't need to have a single data model per entity if that entity must be able to have different "states". As the case with the user "entity" with a state including `password`, `password_hash` and no password.
\ No newline at end of file
diff --git a/docs/tutorial/src/extra-models/tutorial001.py b/docs/tutorial/src/extra-models/tutorial001.py
new file mode 100644 (file)
index 0000000..aa8e7da
--- /dev/null
@@ -0,0 +1,42 @@
+from fastapi import FastAPI
+from pydantic import BaseModel
+from pydantic.types import EmailStr
+
+app = FastAPI()
+
+
+class UserIn(BaseModel):
+    username: str
+    password: str
+    email: EmailStr
+    full_name: str = None
+
+
+class UserOut(BaseModel):
+    username: str
+    email: EmailStr
+    full_name: str = None
+
+
+class UserInDB(BaseModel):
+    username: str
+    hashed_password: str
+    email: EmailStr
+    full_name: str = None
+
+
+def fake_password_hasher(raw_password: str):
+    return "supersecret" + raw_password
+
+
+def fake_save_user(user_in: UserIn):
+    hashed_password = fake_password_hasher(user_in.password)
+    user_in_db = UserInDB(**user_in.dict(), hashed_password=hashed_password)
+    print("User saved! ..not really")
+    return user_in_db
+
+
+@app.post("/user/", response_model=UserOut)
+async def create_user(*, user_in: UserIn):
+    user_saved = fake_save_user(user_in)
+    return user_saved
diff --git a/docs/tutorial/src/extra-models/tutorial002.py b/docs/tutorial/src/extra-models/tutorial002.py
new file mode 100644 (file)
index 0000000..605baf9
--- /dev/null
@@ -0,0 +1,40 @@
+from fastapi import FastAPI
+from pydantic import BaseModel
+from pydantic.types import EmailStr
+
+app = FastAPI()
+
+
+class UserBase(BaseModel):
+    username: str
+    email: EmailStr
+    full_name: str = None
+
+
+class UserIn(UserBase):
+    password: str
+
+
+class UserOut(UserBase):
+    pass
+
+
+class UserInDB(UserBase):
+    hashed_password: str
+
+
+def fake_password_hasher(raw_password: str):
+    return "supersecret" + raw_password
+
+
+def fake_save_user(user_in: UserIn):
+    hashed_password = fake_password_hasher(user_in.password)
+    user_in_db = UserInDB(**user_in.dict(), hashed_password=hashed_password)
+    print("User saved! ..not really")
+    return user_in_db
+
+
+@app.post("/user/", response_model=UserOut)
+async def create_user(*, user_in: UserIn):
+    user_saved = fake_save_user(user_in)
+    return user_saved
index 7770d5fe5896cc8a77b1af181d2a73c6fa1ccfbe..1fc0ce5d1e95f01e49919e666347fb5086e1c7ea 100644 (file)
@@ -30,6 +30,7 @@ nav:
         - Cookie Parameters: 'tutorial/cookie-params.md'
         - Header Parameters: 'tutorial/header-params.md'
         - Response Model: 'tutorial/response-model.md'
+        - Extra Models: 'tutorial/extra-models.md'
     - Concurrency and async / await: 'async.md'
     - Deployment: 'deployment.md'