Pydantic 基本使用

Pydantic 是 Python 中最流行的数据验证库,使用 Python 类型注解来定义数据结构,自动完成数据验证、序列化和文档生成。FastAPI 底层依赖 Pydantic 处理所有请求/响应数据。

官网:https://docs.pydantic.dev

1、安装

pip install pydantic

2、基本模型定义

继承 BaseModel 定义数据模型:

from pydantic import BaseModel

class User(BaseModel):
    name: str
    age: int
    email: str

创建实例

user = User(name="Alice", age=25, email="alice@example.com")
print(user.name)  # Alice

自动类型转换

Pydantic 会自动尝试转换类型:

user = User(name="Bob", age="30", email="bob@example.com")  # age 传入字符串
print(user.age)      # 30 (int)
print(type(user.age))  # <class 'int'>

数据验证

类型不匹配时自动报错:

try:
    user = User(name="Charlie", age="not a number", email="charlie@example.com")
except Exception as e:
    print(e)
    # 1 validation error for User
    # age
    #   Input should be a valid integer...

3、字段类型

Pydantic 支持所有 Python 基本类型和复合类型:

from pydantic import BaseModel
from datetime import datetime
from typing import Optional

class Product(BaseModel):
    name: str                    # 字符串
    price: float                 # 浮点数
    quantity: int = 0            # 整数,默认值 0
    is_active: bool = True       # 布尔值
    tags: list[str] = []         # 字符串列表
    metadata: dict = {}          # 字典
    created_at: datetime         # 日期时间
    description: Optional[str] = None  # 可选字符串

4、可选字段与默认值

可选字段

使用 Optional[T]T | None 声明可选字段:

from typing import Optional

class Item(BaseModel):
    name: str                           # 必填
    description: Optional[str] = None   # 可选,默认 None
    price: float                        # 必填
    tax: float | None = None            # 可选,默认 None(Python 3.10+ 语法)

默认值

class Config(BaseModel):
    host: str = "localhost"    # 默认值
    port: int = 8000           # 默认值
    debug: bool = False        # 默认值

5、字段校验(Field)

使用 Field 添加更详细的校验规则:

from pydantic import BaseModel, Field

class User(BaseModel):
    # 必填,3-50 个字符
    username: str = Field(..., min_length=3, max_length=50, description="用户名")

    # 必填,至少 6 个字符
    password: str = Field(..., min_length=6, description="密码")

    # 可选,最多 100 个字符
    nickname: str | None = Field(None, max_length=100, description="昵称")

    # 必填,大于 0
    age: int = Field(..., gt=0, le=150, description="年龄")

    # 必填,正则校验
    phone: str = Field(..., pattern=r"^1[3-9]\d{9}$", description="手机号")

Field 参数说明

参数说明示例
...必填字段Field(...)
None可选字段Field(None)
min_length最小长度Field(min_length=3)
max_length最大长度Field(max_length=50)
gt大于Field(gt=0)
ge大于等于Field(ge=0)
lt小于Field(lt=100)
le小于等于Field(le=100)
pattern正则表达式Field(pattern=r"^\d+$")
description字段描述Field(description="用户名")
example示例值Field(example="Alice")

6、Email 和 URL 校验

from pydantic import BaseModel, EmailStr, HttpUrl

class Contact(BaseModel):
    email: EmailStr              # 邮箱格式校验
    website: HttpUrl | None = None  # URL 格式校验
    homepage: str | None = None

# pip install pydantic[email]
contact = Contact(email="alice@example.com", website="https://example.com")

7、嵌套模型

模型可以嵌套使用:

from pydantic import BaseModel

class Address(BaseModel):
    city: str
    street: str
    zip_code: str

class User(BaseModel):
    name: str
    age: int
    address: Address              # 嵌套模型
    tags: list[str] = []

user = User(
    name="Alice",
    age=25,
    address={"city": "Beijing", "street": "Main St", "zip_code": "100000"}
)
print(user.address.city)  # Beijing

8、模型方法

model_dump() - 转换为字典

user = User(name="Alice", age=25, address=Address(city="Beijing", street="Main St", zip_code="100000"))
data = user.model_dump()
print(data)
# {'name': 'Alice', 'age': 25, 'address': {'city': 'Beijing', 'street': 'Main St', 'zip_code': '100000'}, 'tags': []}

model_dump_json() - 转换为 JSON 字符串

json_str = user.model_dump_json()
print(json_str)
# {"name":"Alice","age":25,"address":{"city":"Beijing","street":"Main St","zip_code":"100000"},"tags":[]}

model_validate() - 从字典验证

data = {"name": "Bob", "age": 30, "address": {"city": "Shanghai", "street": "Second St", "zip_code": "200000"}}
user = User.model_validate(data)
print(user.name)  # Bob

model_validate_json() - 从 JSON 字符串验证

json_str = '{"name": "Charlie", "age": 35, "address": {"city": "Guangzhou", "street": "Third St", "zip_code": "510000"}}'
user = User.model_validate_json(json_str)
print(user.name)  # Charlie

9、模型继承

from pydantic import BaseModel

class BaseUser(BaseModel):
    username: str
    email: str

class CreateUser(BaseUser):
    password: str

class UpdateUser(BaseModel):
    username: str | None = None
    email: str | None = None

class UserResponse(BaseUser):
    id: int
    is_active: bool = True

10、枚举类型

from enum import Enum
from pydantic import BaseModel

class UserRole(str, Enum):
    ADMIN = "admin"
    USER = "user"
    GUEST = "guest"

class User(BaseModel):
    name: str
    role: UserRole = UserRole.USER

user = User(name="Alice", role="admin")
print(user.role)  # UserRole.ADMIN

11、自定义校验器

使用 @field_validator 添加自定义校验逻辑:

from pydantic import BaseModel, field_validator

class User(BaseModel):
    username: str
    password: str
    confirm_password: str

    @field_validator("username")
    @classmethod
    def username_must_be_alphanumeric(cls, v: str) -> str:
        if not v.isalnum():
            raise ValueError("用户名只能包含字母和数字")
        return v

    @field_validator("confirm_password")
    @classmethod
    def passwords_match(cls, v: str, info) -> str:
        if "password" in info.data and v != info.data["password"]:
            raise ValueError("两次密码不一致")
        return v

12、FastAPI 中的使用

请求体校验

from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI()

class UserCreate(BaseModel):
    username: str = Field(..., min_length=3, max_length=20)
    password: str = Field(..., min_length=6)
    email: str | None = None

@app.post("/users/")
async def create_user(user: UserCreate):
    return {"username": user.username}

响应模型

class UserResponse(BaseModel):
    id: int
    username: str
    email: str | None = None

@app.get("/users/{user_id}", response_model=UserResponse)
async def get_user(user_id: int):
    return {"id": user_id, "username": "Alice", "email": "alice@example.com", "password": "secret"}
    # 返回结果中不会包含 password 字段

查询参数校验

from fastapi import Query

@app.get("/items/")
async def list_items(
    page: int = Query(1, ge=1, description="页码"),
    page_size: int = Query(10, ge=1, le=100, description="每页数量"),
    keyword: str | None = Query(None, max_length=50, description="搜索关键词"),
):
    return {"page": page, "page_size": page_size, "keyword": keyword}

13、Pydantic Settings(配置管理)

使用 pydantic-settings 管理环境变量:

pip install pydantic-settings
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    APP_NAME: str = "My App"
    DEBUG: bool = False
    DATABASE_URL: str
    SECRET_KEY: str
    ACCESS_TOKEN_EXPIRE_MINUTES: int = 30

    model_config = {"env_file": ".env", "extra": "ignore"}

settings = Settings()
print(settings.DATABASE_URL)

.env 文件:

APP_NAME=FastAPI Admin
DEBUG=true
DATABASE_URL=mongodb://localhost:27017
SECRET_KEY=your-secret-key

总结

功能说明
BaseModel定义数据模型
Field字段校验和元数据
Optional[T]可选字段
EmailStr邮箱格式校验
HttpUrlURL 格式校验
@field_validator自定义校验器
model_dump()转换为字典
model_dump_json()转换为 JSON
model_validate()从字典验证
BaseSettings环境变量配置管理

参考