Pydantic Schema — Data Validation and Serialization
In this chapter, you will learn about Pydantic Schema and understand its separation of responsibilities from ORM models—this is FastAPI's most core feature.
Why do we need Schema?
ORM models (models.py) define the database structure, but cannot be directly used for API request/response validation.
Reason: ORM models contain database-specific logic (such as relationships), and you don't want to expose all fields to the API.
Pydantic SchemaIt is a pure data class, specifically responsible for API input/output validation and serialization.
The relationship among these three:
客户端 → [Pydantic Schema 校验] → 路由函数 → [ORM 模型] → 数据库 客户端 ← [Pydantic Schema 序列化] ← 路由函数 ← [ORM 模型] ← 数据库
Pydantic Schema Basics
Pydantic V2 is FastAPI's data engine, defining data structures through type hints.
Example
class ItemCreate(BaseModel):
"""Schema for creating requests"""
name: str
price: float # Automatically validate type
is_offer: bool = False # With default values (optional fields)
class ItemResponse(BaseModel):
"""Response Schema"""
id: int
name: str
price: float
# V2 approach: enable ORM mode, allowing direct conversion from ORM objects.
model_config = ConfigDict(from_attributes=True)
Define the Schema for the blog
Example
from pydantic import BaseModel, ConfigDict
from datetime import datetime
from typing import Optional
# ====== Category Schema ======
class CategoryBase(BaseModel):
name: str
slug: str
class CategoryResponse(CategoryBase):
id: int
model_config = ConfigDict(from_attributes=True)
# ====== Post Schema ======
class PostBase(BaseModel):
title: str
slug: str
summary: Optional[str] = None # Optional = can be None
content: str
category_id: int
class PostCreate(PostBase):
"""Request body Schema for creating an article."""
pass # Directly inherits PostBase, fields are identical.
class PostUpdate(BaseModel):
"""Update article (all fields optional)"""
title: Optional[str] = None
slug: Optional[str] = None
summary: Optional[str] = None
content: Optional[str] = None
category_id: Optional[int] = None
class PostResponse(PostBase):
"""Response Schema returned to the client."""
id: int
created_at: datetime
updated_at: datetime
category: CategoryResponse # Nested Schema: Return category information
model_config = ConfigDict(from_attributes=True)
from_attributes=True tells Pydantic: this Schema can be created from ORM objects
In Pydantic V2,
model_config = ConfigDict(from_attributes=True)Replaces the one in V1class Config: orm_mode = TrueThis configuration allows you to directly build a Pydantic Schema from SQLAlchemy ORM objects:PostResponse.model_validate(post_orm_obj)。
Using Schema in routes
Example
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from database import get_db
from models import Post
from schemas import PostCreate, PostResponse
router = APIRouter(prefix="/posts", tags=["Article"])
# After declaring response_model=PostResponse:
# 1. FastAPI automatically converts ORM objects to Schema (serialization)
# 2. API documentation automatically displays the response structure
@router.post("/", response_model=PostResponse, status_code=201)
def create_post(post_data: PostCreate, db: Session = Depends(get_db)):
"""
Create new article
- post_data: request body, FastAPI automatically validates against the PostCreate Schema
db: database session injected via Depends(get_db)
"""
post = Post(**post_data.model_dump()) # model_dump() converts Schema to dict
db.add(post)
db.commit()
db.refresh(post) # Refresh and get the database auto-increment ID
return post # FastAPI automatically serializes using PostResponse.
response_modelIt is one of FastAPI's strongest features. Once declared, FastAPI will: 1. Automatically filter out fields from ORM objects that are not declared in the Schema; 2. Automatically convert types such as datetime into JSON strings; 3. Generate precise OpenAPI documentation.
ORM Model vs Pydantic Schema Comparison
| Aspect | SQLAlchemy ORM Model | Pydantic Schema |
|---|---|---|
| Definition location | models.py | schemas.py |
| base class | Base (DeclarativeBase) | BaseModel (Pydantic) |
| Responsibilities | Define database table structures and relationships | Define API input/output formats and validation rules. |
| Relationship with database | Directly maps to database tables | It doesn't care about the database, only about the API contract. |
| Field declaration | Column(Integer, ...) | Python type hints: int, str |
| serialization | Cannot be directly converted to JSON | Built-in .model_dump() and JSON serialization |
| Conversion direction | ORM → Schema:model_validate(obj) | Schema → ORM:Model(**schema.model_dump()) |
Chapter summary
In this chapter, you have mastered FastAPI's most core Pydantic Schema concepts: BaseModel for defining request/response Schemas, from_attributes=True to enable ORM mode, response_model for automatically serializing ORM objects, and the principle of separating responsibilities between Schemas and ORM models.
This is the essence of FastAPI's type-driven architecture—define the data shape first, and routes and documentation are generated automatically.
other extensions