Data Models

Notes Single

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:

  1. Automatically added to API documentation
  2. Automatically convert input data to declared types
  3. Determine the required data
  4. Limit the data that is sent in the output
  5. 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.

Related Posts: