In this unit, we will implement a demo program that includes many Pydantic models for specific use-cases:
The input model needs to be able to have a password.
The output model should not have a password.
The database model would probably need to store a hashed password.
Example program
from fastapi import FastAPI
from pydantic import BaseModel, 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 UserDB(BaseModel):
username: str
hashed_password: str
email: EmailStr
full_name: str = None
def fake_password_hasher(raw_password: str):
"""This is a fake password hasher, don't use it in production."""
return "salted_" + raw_password
def fake_save_user(user_in: UserIn):
hashed_password = fake_password_hasher(user_in.password)
user_db = UserDB(**user_in.model_dump(), hashed_password=hashed_password)
print("User has been saved to an imaginary database!")
return user_db
@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn):
user_saved = fake_save_user(user)
return user_saved
Within function definitions, the double asterisk, **, is used in conjunction with the parameter name **kwargs (though kwargs itself is not mandatory). This allows you to accept a variable number of keyword arguments when the function is called.
Reduce code duplication
In the above program, there are code snippets duplicated across class definition. As code duplication increases the chances of bugs, security issues, code desynchronization issues (when you update in one place but not in the others), etc.
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserBase(BaseModel):
username: str
email: EmailStr
full_name: str = None
class UserIn(UserBase):
password: str
class UserOut(UserBase):
pass
class UserDB(UserBase):
hashed_password: str
def fake_password_hasher(raw_password: str):
"""This is a fake password hasher, don't use it in production."""
return "salted_" + raw_password
def fake_save_user(user_in: UserIn):
hashed_password = fake_password_hasher(user_in.password)
user_db = UserDB(**user_in.model_dump(), hashed_password=hashed_password)
print("User has been saved to an imaginary database!")
return user_db
@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn):
user_saved = fake_save_user(user)
return user_saved