Extra Models
Learn to create multiple related models for different use cases, reduce code duplication through inheritance, and handle Union types for flexible responses.
🎯 What You'll Learn
- •Create separate input, output, and database models for security
- •Use model inheritance to reduce code duplication
- •Handle Union types for flexible response models
- •Understand model unpacking and data conversion patterns
- •Apply proper separation of concerns in API design
Extra Models
🎯 What You'll Learn
- Create separate input, output, and database models for security
- Use model inheritance to reduce code duplication
- Handle Union types for flexible response models
- Understand model unpacking and data conversion patterns
- Apply proper separation of concerns in API design
📚 Theory
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 not have a password.
- The database model would probably need to have a hashed password.
Never store user's plaintext passwords. Always store a "secure hash" that you can then verify.
🔧 Key Concepts
Multiple Models for Different Use Cases
Here's how the models could look like with their password fields and the places where they are used:
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
class UserInDB(BaseModel):
username: str
hashed_password: str
email: EmailStr
full_name: str | None = None
Model Unpacking and Data Conversion
You can create a Pydantic model from the contents of another using the model_dump() method and unpacking:
def fake_save_user(user_in: UserIn):
hashed_password = fake_password_hasher(user_in.password)
user_in_db = UserInDB(**user_in.model_dump(), hashed_password=hashed_password)
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
Reducing Code Duplication with Inheritance
You can declare a UserBase model that serves as a base for other models. Then make subclasses that inherit its attributes:
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
Union Types for Flexible Responses
You can declare a response to be the Union of two or more types:
from typing import Union
@app.get("/items/{item_id}")
async def read_item(item_id: str) -> Union[PlaneItem, CarItem]:
# Return either a PlaneItem or CarItem based on logic
pass
💡 Best Practices
- Create separate models for input, output, and database operations to maintain security and clarity
- Use model inheritance to reduce code duplication while keeping models focused
- Always filter sensitive data like passwords from output models using
response_model - Use Union types when endpoints can legitimately return different model types
- Include the most specific type first in Union declarations for better type checking
🔗 Additional Resources
💡 Hint
Create a base model class and inherit from it for different use cases. Use Union types when endpoints can return different model types. Never expose sensitive data like passwords in output models.
Ready to Practice?
Now that you understand the theory, let's put it into practice with hands-on coding!
Start Interactive Lesson