mirror of https://github.com/tiangolo/fastapi.git
📝 Add docs for extra models
This commit is contained in:
parent
222b75ac0c
commit
99c7ebdb6b
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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'
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue