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 v1Pydantic v2
ORM 模式orm_mode = Truefrom_attributes = True
模型配置class Config:model_config = {}
验证器@validator@field_validator
安装pip install pydanticpip 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 + Pydanticorm
自动生成 TypeScript 类型typescript
模型继承inheritance
动态模型create_model
自定义序列化json_encoders

现在就运行你的模型,打开 /docs 查看自动生成的示例和验证吧!

需要我生成一个 完整用户管理系统(带注册、登录、响应分离) 的 Pydantic 模板吗?
回复 pydantic模板 立刻获取!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注