Data Models
BaseModel
To manage the application models, you can use classes that inherit from BaseModel (Pydantic).
In the following example, there is a class UserIn which is used for receiving the user data from POST method at route '/users/', the function of route defines the parameter 'user' is type UserIn (class declared above in the code), and at the decorator '@app.post("/user/", response_model=UserOut)' is defined that the output is type UserOut (class declared above in the code). This permits to control the fields that get in and the fields that get out.
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: str | None = None
class UserOut(BaseModel):
username: str
email: EmailStr
full_name: str | None = None
@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn):
return user
Multiple classes for a unique entity
For writing less repeated code, define a base class and other classes that inherit from it, which would be used in certain scenarios, let's see an example:
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserBase(BaseModel):
username: str
email: EmailStr
full_name: str | None = 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
Then, the class 'UserBase' is defined, but later there are 3 classes that inherit from it (UserIn, UserOut, UserInDB) which are applied in each context.
Having classes that inherit from BaseModel (pydantic) is important because:
- Automatically added to API documentation
- Automatically convert input data to declared types
- Determine the required data
- Limit the data that is sent in the output
- Serializes the data so that it can be sent as JSON
For watching the models declared in the documentation, run the server and enter the url 'localhost:8000/docs':
Real products are complex applications, and it is not a good idea having all models of all entities and all routes, everything in the same file, the let's move the model to another directory:
File: main.py
from fastapi import FastAPI
from models.user import UserIn,UserOut,fake_save_user
app = FastAPI()
@app.post("/user/", response_model=UserOut)
async def create_user(user_in: UserIn):
user_saved = fake_save_user(user_in)
return user_saved
File: models/user.py
from pydantic import BaseModel, EmailStr
class UserBase(BaseModel):
username: str
email: EmailStr
full_name: str | None = 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
Thanks for reading :)
I invite you to continue reading other entries and visiting us again soon.