Fastapi Pydantic
Pydantic is a core dependency of FastAPI, used for data validation and serialization. It allows you to define data models using standard Python type annotations, automatically performing data validation, type conversion, and documentation generation.
* * *
## What is Pydantic
Pydantic is a Python data validation library, and its core idea is: **Define data structures using Python type annotations, and Pydantic automatically handles validation and conversion**. In FastAPI, Pydantic is mainly used for:
| Usage | Description |
| --- | --- |
| Request Body Validation | Automatically validates whether client-sent JSON data matches the model definition |
| Response Body Serialization | Automatically converts model data to JSON responses |
| Auto Documentation | Model fields, types, and validation rules automatically appear in API documentation |
| Editor Support | Model properties get full auto-completion in the editor |
* * *
## Define Pydantic Models
Create a class that inherits from `BaseModel`, using standard Python type declarations for fields:
## Example
from pydantic import BaseModel
class Item(BaseModel):
name: str# Required: item name
description: str | None=None# Optional: item description
price: float# Required: item price
tax: float | None=None# Optional: tax
Whether a field is required depends on whether it has a default value:
| Field | Declaration | Required |
| --- | --- | --- |
| `name` | `name: str` | Required |
| `description` | `description: str | None = None` | Optional |
| `price` | `price: float` | Required |
| `tax` | `tax: float | None = None` | Optional |
* * *
## Use Pydantic Models
### As Request Body
The most common usage is to declare the model as a parameter for the path operation function:
## Example
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):
# FastAPI automatically validates the request body; after validation passes, it assigns to the item parameter
return item
### Access and Manipulate Model Data
## Example
@app.post("/items/")
async def create_item(item: Item):
# Access model properties
print(item.name)# Access property directly
print(item.price)# Editor provides auto-completion
# Serialize to dictionary
item_dict = item.model_dump()
print(item_dict)# {"name": "Foo", "description": None, "price": 45.2, "tax": None}
# Serialize to JSON string
item_json = item.model_dump_json()
print(item_json)# '{"name":"Foo","description":null,"price":45.2,"tax":null}'
return item_dict
> Pydantic v2 uses `model_dump()` and `model_dump_json()` to replace v1's `dict()` and `json()` methods. The new methods have better performance (implemented in Rust under the hood).
* * *
## Model Validation Example
When the client sends data that doesn't match the model definition, FastAPI returns detailed validation errors:
## Example
# Client sends data with missing required fields
POST /items/
{
"description": "Missing name and price"
}
# FastAPI returns validation errors
{
"detail": [
{
"type": "missing",
"loc": ["body","name"],
"msg": "Field required",
"input": {"description": "Missing name and price"}
},
{
"type": "missing",
"loc": ["body","price"],
"msg": "Field required",
"input": {"description": "Missing name and price"}
}
]
}
If data with type errors is sent:
// price should be a number, but a string was passed{ "name": "Foo", "price": "not a number"}// FastAPI returns the error{ "detail": [ { "type": "float_parsing", "loc": ["body", "price"], "msg": "Input should be a valid number, unable to parse string as a number", "input": "not a number" } ]}
* * *
## Common Pydantic v2 Methods
| Method | v2 (Recommended) | v1 (Deprecated) | Description |
| --- | --- | --- | --- |
| Serialize to dict | `item.model_dump()` | `item.dict()` | Convert model to Python dictionary |
| Serialize to JSON | `item.model_dump_json()` | `item.json()` | Convert model to JSON string |
| Create from dict | `Item.model_validate(data)` | `Item.parse_obj(data)` | Create and validate model from dictionary |
| Create from JSON | `Item.model_validate_json(json_str)` | `Item.parse_raw(json_str)` | Create model from JSON string |
| Get JSON Schema | `Item.model_json_schema()` | `Item.schema()` | Get model's JSON Schema |
* * *
## Model Configuration (model_config)
Pydantic v2 uses `model_config` to replace v1's internal `Config` class:
## Example
from pydantic import BaseModel, ConfigDict
class Item(BaseModel):
name: str
price: float
# Pydantic v2 configuration method
model_config = ConfigDict(
json_schema_extra={# Show examples in API documentation
"examples": [
{
"name": "Foo",
"price": 35.4
}
]
}
)
> In Pydantic v2, `orm_mode = True` has been changed to `from_attributes = True`, `schema_extra` has been changed to `json_schema_extra`, and `allow_population_by_field_name` has been changed to `populate_by_name`.
* * *
## Model Inheritance
Pydantic models support inheritance, making it easy to create input models and output models:
## Example
from pydantic import BaseModel, EmailStr
# Base model
class UserBase(BaseModel):
username: str# Required
email: EmailStr # Required, automatically validates email format
full_name: str | None=None# Optional
# Input model when creating a user (includes password)
class UserCreate(UserBase):
password: str# Required
# Output model when returning user info (excludes password)
class UserOut(UserBase):
id: int# Generated by server
# Usage example
@app.post("/users/", response_model=UserOut)
async def create_user(user: UserCreate):
# Function receives UserCreate (includes password), but response uses UserOut (excludes password)
# This way the password won't appear in the API response
return{"id": 1, **user.model_dump(exclude={"password"})}
> Using different input and output models is an important way to protect sensitive data. Never return sensitive fields like passwords in API responses.
* * *
## Summary
Key points of Pydantic models:
* Use `BaseModel` to define data models, with standard Python type declarations for fields
* Fields with default values are optional, fields without default values are required
* v2 uses new methods like `model_dump()` and `model_validate()`
* Use model inheritance to create different input/output models to protect sensitive data
* All validation and documentation is automatic - just declare types once
YouTip