Data Modeling (Pydantic)#
This page explains how to use Pydantic’s BaseModel in FastAPI to define request bodies, apply validation rules, compose nested models, and control response output with response_model.
Topic cover:
Request Body
Pydantic BaseModel
Advanced Validation
Nested Models (Body)
response_model to filter output
1. Introduction & Setup (15 min)#
Objectives#
Set up a FastAPI project environment.
Understand what Pydantic and FastAPI do for data validation.
Content#
overview:
FastAPI is a high-performance web framework for building APIs with Python 3.7+.
It automatically handles data parsing, validation, and documentation using Pydantic and OpenAPI.
Setup Instructions#
# Create and activate a virtual environment
python -m venv venv
source venv/bin/activate # (Windows: venv\Scripts\activate)
# Install dependencies
pip install fastapi uvicorn
Starter App#
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"message": "Hello, FastAPI!"}
Run the server:
uvicorn main:app --reload
Exercise (5 min)#
Run the app and open:
http://127.0.0.1:8000/docs
2. Request Body (30 min)#
Objectives#
Learn how to handle incoming JSON request bodies.
Understand FastAPI’s automatic request parsing.
Content#
Example: Handling a Simple Request Body#
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.post("/items/")
async def create_item(item: Item):
return {"received": item}
What happens:
FastAPI reads and parses the JSON body.
Converts it into a Pydantic model (
Item).Returns structured JSON automatically.
Exercise (10 min)#
Task:
Create an endpoint /users/ that accepts a user object with username, email, and optional age.
3. Pydantic BaseModel (40 min)#
Objectives#
Understand how
BaseModelworks.Learn type hints, default values, and field customization.
Key Concepts#
Type validation:
str,int,float,bool, etc.Optional fields using
Optionalor| None.Field constraints via
Field.
Example: Using Field for Metadata#
from pydantic import BaseModel, Field
class Product(BaseModel):
name: str = Field(..., min_length=3, max_length=50)
price: float = Field(..., gt=0)
in_stock: bool = Field(default=True)
Example Endpoint#
@app.post("/products/")
async def create_product(product: Product):
return product
Exercise (15 min)#
Create a model
Bookwith fields:title(min_length=2)authorpages(must be > 0)published(bool, defaultTrue)
POST
/books/→ return the same data.
4. Advanced Validation (50 min)#
Objectives#
Use validators to apply custom validation logic.
Learn root validators and complex data checks.
Example: Field Validators#
from pydantic import validator
class User(BaseModel):
username: str
password: str
@validator("password")
def password_strength(cls, v):
if len(v) < 8:
raise ValueError("Password must be at least 8 characters long")
return v
Example: Root Validators#
from pydantic import root_validator
class Order(BaseModel):
price: float
quantity: int
total: float
@root_validator
def check_total(cls, values):
price, quantity, total = values.get('price'), values.get('quantity'), values.get('total')
if total != price * quantity:
raise ValueError("Total must equal price × quantity")
return values
Exercise (20 min)#
Build a model
Registrationwith:email(must contain “@”)password(min 8 chars)confirm_password
Use a root validator to ensure passwords match.
5. Nested Models (Body) (40 min)#
Objectives#
Learn how to use nested Pydantic models for complex request bodies.
Understand how FastAPI automatically parses nested data.
Example: Nested Models#
class Address(BaseModel):
city: str
state: str
zip_code: str
class UserProfile(BaseModel):
name: str
email: str
address: Address
@app.post("/profile/")
async def create_profile(profile: UserProfile):
return profile
Exercise (20 min)#
Create a nested model for an Order:
Customer(name, email)Item(name, price, quantity)Order(customer:Customer, items: List[Item])
POST
/orders/→ Return total order amount.
6. Using response_model to Filter Output (40 min)#
Objectives#
Use
response_modelto structure or filter API responses.Hide sensitive fields such as passwords.
Example#
class UserIn(BaseModel):
username: str
password: str
email: str
class UserOut(BaseModel):
username: str
email: str
@app.post("/users/", response_model=UserOut)
async def create_user(user: UserIn):
# Simulate saving user to DB
return user
What happens:
The request accepts all fields from
UserIn.The response only returns fields from
UserOut.
Exercise (15 min)#
Create:
UserIn: includespasswordUserOut: excludespassword
POST
/users/register→ returns safe public data.
7. Recap Practice (30 min)#
Summary#
FastAPI automatically validates request bodies.
Pydantic models enforce structure and constraints.
Validators allow for custom logic.
Nested models handle complex payloads.
response_modelprotects and structures output.
Final Challenge (Optional)#
Build a small API for a Library System:
Book,Author, andPublishermodels.POST
/books/with nested models.Use
response_modelto hide internal IDs.