FastAPI Pydantic 模型
FastAPI + Pydantic 模型完全指南
Pydantic 是 FastAPI 的 核心引擎,负责:
- 数据验证(类型、格式、必填)
- 数据序列化(Python ↔ JSON)
- 自动生成 API 文档
- 类型安全(配合 Python 类型提示)
一、Pydantic 模型基础
from pydantic import BaseModel
from typing import Optional, List
from datetime import datetime
class User(BaseModel):
id: int
username: str
email: str
age: Optional[int] = None
is_active: bool = True
tags: List[str] = []
created_at: datetime = datetime.now()
二、核心特性
| 特性 | 说明 |
|---|---|
| 类型检查 | int, str, float, bool, list, dict, datetime, UUID 等 |
| 默认值 | = None, = [], = "default" |
| 可选字段 | Optional[str] = None |
| 嵌套模型 | 模型中包含其他模型 |
| 验证器 | @validator, @root_validator |
| 自动转换 | "123" → 123(int) |
| 错误提示 | 422 错误,详细字段错误 |
三、FastAPI 中使用 Pydantic
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, EmailStr, Field, PositiveInt
from typing import List, Optional
app = FastAPI()
# 1. 创建用户(请求体)
class UserCreate(BaseModel):
username: str = Field(..., min_length=3, max_length=20, description="用户名")
email: EmailStr = Field(..., description="邮箱地址")
age: PositiveInt = Field(..., ge=1, le=120, description="年龄")
bio: Optional[str] = Field(None, max_length=500)
# 2. 返回用户(响应模型)
class UserResponse(BaseModel):
id: int
username: str
email: str
age: int
bio: Optional[str] = None
created_at: datetime
class Config:
from_attributes = True # 支持 ORM 模式(SQLAlchemy)
# 模拟数据库
fake_db = []
@app.post("/users/", response_model=UserResponse, status_code=201)
def create_user(user: UserCreate):
new_user = {
"id": len(fake_db) + 1,
**user.dict(),
"created_at": datetime.now()
}
fake_db.append(new_user)
return new_user
四、Field() 详细参数
Field(
default=..., # 默认值(... 表示必填)
alias="userName", # JSON 字段名(前端用 userName)
title="用户名",
description="用户登录名,3-20个字符",
example="alice",
min_length=3,
max_length=20,
regex=r"^[a-zA-Z0-9_]+$",
gt=0, lt=100, # 数值范围
ge=1, le=120
)
五、嵌套模型
class Address(BaseModel):
street: str
city: str
zip_code: str
class UserWithAddress(UserResponse):
address: Address
addresses: List[Address] = []
# 使用
@app.post("/users/with-address/")
def create_with_address(data: UserWithAddress):
return data
六、验证器(@validator)
from pydantic import validator
class User(BaseModel):
username: str
password: str
@validator('username')
def username_alphanumeric(cls, v):
if not v.isalnum():
raise ValueError('用户名必须是字母数字')
return v.lower()
@validator('password')
def password_strength(cls, v):
if len(v) < 8:
raise ValueError('密码至少8位')
if not any(c.isupper() for c in v):
raise ValueError('密码必须包含大写字母')
return v
七、根验证器(@root_validator)
from pydantic import root_validator
class User(BaseModel):
age: int
is_adult: bool
@root_validator
def check_adult(cls, values):
age = values.get('age')
is_adult = values.get('is_adult')
if age and is_adult is not None:
if age >= 18 and not is_adult:
raise ValueError('年龄≥18必须是成人')
if age < 18 and is_adult:
raise ValueError('年龄<18不能是成人')
return values
八、模型配置(Config)
class User(BaseModel):
password: str
class Config:
from_attributes = True # 替代 orm_mode(Pydantic v2)
extra = "forbid" # 禁止多余字段
# extra = "allow" # 允许多余字段
# extra = "ignore" # 忽略多余字段
json_encoders = {
datetime: lambda v: v.isoformat()
}
九、请求体示例(在文档中显示)
class Item(BaseModel):
name: str
price: float
model_config = {
"json_schema_extra": {
"examples": [
{
"name": "Apple",
"price": 1.5
},
{
"name": "Book",
"price": 29.99
}
]
}
}
效果:在 Swagger UI 中显示多个示例
十、响应模型排除字段
class UserInDB(BaseModel):
id: int
username: str
email: str
password_hash: str
class UserOut(BaseModel):
id: int
username: str
email: str
@app.get("/users/{id}", response_model=UserOut)
def get_user():
return UserInDB(
id=1,
username="john",
email="john@example.com",
password_hash="xxx" # 不会返回
)
高级排除:
response_model=UserOut,
response_model_exclude={"password_hash"},
response_model_include={"id", "username"}
十一、Pydantic v1 vs v2 对比
| 特性 | Pydantic v1 | Pydantic v2 |
|---|---|---|
| ORM 模式 | orm_mode = True | from_attributes = True |
| 模型配置 | class Config: | model_config = {} |
| 验证器 | @validator | @field_validator |
| 安装 | pip install pydantic | pip install pydantic>=2.0 |
FastAPI 最新版默认使用 Pydantic v2
十二、完整实战示例
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, EmailStr, Field, field_validator, model_validator
from typing import List, Optional
from datetime import datetime
import re
app = FastAPI()
class UserCreate(BaseModel):
username: str = Field(..., min_length=3, max_length=20)
email: EmailStr
password: str = Field(..., min_length=8)
confirm_password: str
age: Optional[int] = Field(None, ge=1, le=120)
@field_validator('username')
@classmethod
def validate_username(cls, v):
if not re.match(r"^[a-zA-Z0-9_]+$", v):
raise ValueError('用户名只能包含字母、数字、下划线')
return v
@model_validator(mode='after')
def check_passwords_match(self):
if self.password != self.confirm_password:
raise ValueError('两次密码不一致')
return self
class User(UserCreate):
id: int
created_at: datetime
model_config = {
"from_attributes": True,
"json_schema_extra": {
"examples": [{
"username": "alice",
"email": "alice@example.com",
"password": "Secret123",
"confirm_password": "Secret123",
"age": 25
}]
}
}
fake_db = []
@app.post("/users/", response_model=User, status_code=201)
def create_user(user: UserCreate):
if any(u["username"] == user.username for u in fake_db):
raise HTTPException(400, "用户名已存在")
new_user = {
"id": len(fake_db) + 1,
**user.model_dump(exclude={"confirm_password"}),
"created_at": datetime.now()
}
fake_db.append(new_user)
return User(**new_user)
十三、常见错误处理(422)
{
"detail": [
{
"loc": ["body", "email"],
"msg": "value is not a valid email address",
"type": "value_error.email"
},
{
"loc": ["body", "age"],
"msg": "ensure this value is greater than 0",
"type": "value_error.number.not_gt"
}
]
}
总结:Pydantic 最佳实践
| 实践 | 建议 |
|---|---|
| 请求/响应分离 | UserCreate vs UserResponse |
| 使用 Field() | 添加描述、示例、验证 |
| 响应模型过滤 | 隐藏密码、内部字段 |
| 开启 from_attributes | 配合 SQLAlchemy |
| 使用示例 | json_schema_extra.examples |
| 验证器 | 复杂业务逻辑 |
下一步学习
| 主题 | 关键词 |
|---|---|
| SQLAlchemy + Pydantic | orm |
| 自动生成 TypeScript 类型 | typescript |
| 模型继承 | inheritance |
| 动态模型 | create_model |
| 自定义序列化 | json_encoders |
现在就运行你的模型,打开 /docs 查看自动生成的示例和验证吧!
需要我生成一个 完整用户管理系统(带注册、登录、响应分离) 的 Pydantic 模板吗?
回复 pydantic模板 立刻获取!