pydantic
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePydantic Validation Skill
Pydantic验证技能
Summary
概述
Python data validation using type hints and runtime type checking with Pydantic v2's Rust-powered core for high-performance validation.
借助Pydantic v2的Rust驱动核心,使用类型提示和运行时类型检查实现高性能的Python数据验证。
When to Use
适用场景
- API request/response validation (FastAPI, Django)
- Settings and configuration management (env variables, config files)
- ORM model validation (SQLAlchemy integration)
- Data parsing and serialization (JSON, dict, custom formats)
- Type-safe data classes with automatic validation
- CLI argument parsing with type safety
- API请求/响应验证(FastAPI、Django)
- 设置与配置管理(环境变量、配置文件)
- ORM模型验证(SQLAlchemy集成)
- 数据解析与序列化(JSON、字典、自定义格式)
- 带自动验证的类型安全数据类
- 带类型安全的CLI参数解析
Quick Start
快速开始
python
from pydantic import BaseModel, Field, EmailStr
from datetime import datetime
class User(BaseModel):
id: int
name: str = Field(..., min_length=1, max_length=100)
email: EmailStr
created_at: datetime = Field(default_factory=datetime.now)
is_active: bool = Truepython
from pydantic import BaseModel, Field, EmailStr
from datetime import datetime
class User(BaseModel):
id: int
name: str = Field(..., min_length=1, max_length=100)
email: EmailStr
created_at: datetime = Field(default_factory=datetime.now)
is_active: bool = TrueValidate data
验证数据
user = User(id=1, name="Alice", email="alice@example.com")
print(user.model_dump()) # {'id': 1, 'name': 'Alice', ...}
user = User(id=1, name="Alice", email="alice@example.com")
print(user.model_dump()) # {'id': 1, 'name': 'Alice', ...}
Automatic type coercion
自动类型转换
user2 = User(id="2", name="Bob", email="bob@example.com")
assert user2.id == 2 # String "2" coerced to int
user2 = User(id="2", name="Bob", email="bob@example.com")
assert user2.id == 2 # 字符串"2"转换为整数
Validation error
验证错误
try:
User(id=3, name="", email="invalid")
except ValidationError as e:
print(e.errors())
---try:
User(id=3, name="", email="invalid")
except ValidationError as e:
print(e.errors())
---Core Concepts
核心概念
BaseModel Foundation
BaseModel基础
python
from pydantic import BaseModel, ConfigDict
class Product(BaseModel):
model_config = ConfigDict(
str_strip_whitespace=True,
validate_assignment=True,
use_enum_values=True,
arbitrary_types_allowed=False
)
name: str
price: float
quantity: int = 0python
from pydantic import BaseModel, ConfigDict
class Product(BaseModel):
model_config = ConfigDict(
str_strip_whitespace=True,
validate_assignment=True,
use_enum_values=True,
arbitrary_types_allowed=False
)
name: str
price: float
quantity: int = 0Usage
使用示例
product = Product(name=" Widget ", price=19.99)
assert product.name == "Widget" # Whitespace stripped
product = Product(name=" Widget ", price=19.99)
assert product.name == "Widget" # 自动去除空格
Validate on assignment
赋值时验证
product.price = "29.99" # Auto-converts to float
undefinedproduct.price = "29.99" # 自动转换为浮点数
undefinedField Configuration
字段配置
python
from pydantic import Field, field_validator
from typing import Annotated
class Item(BaseModel):
# Field constraints
sku: str = Field(pattern=r'^[A-Z]{3}-\d{4}$')
price: float = Field(gt=0, le=10000)
stock: int = Field(ge=0, default=0)
# Annotated types (Pydantic v2)
quantity: Annotated[int, Field(ge=1, le=100)]
# Descriptions and examples
description: str = Field(
...,
description="Product description",
examples=["High-quality widget"]
)
# Deprecated fields
old_field: str | None = Field(None, deprecated=True)
@field_validator('sku')
@classmethod
def validate_sku(cls, v: str) -> str:
if not v.startswith('ABC'):
raise ValueError('SKU must start with ABC')
return vpython
from pydantic import Field, field_validator
from typing import Annotated
class Item(BaseModel):
# 字段约束
sku: str = Field(pattern=r'^[A-Z]{3}-\d{4}$')
price: float = Field(gt=0, le=10000)
stock: int = Field(ge=0, default=0)
# 注解类型(Pydantic v2)
quantity: Annotated[int, Field(ge=1, le=100)]
# 描述与示例
description: str = Field(
...,
description="产品描述",
examples=["高品质小部件"]
)
# 已废弃字段
old_field: str | None = Field(None, deprecated=True)
@field_validator('sku')
@classmethod
def validate_sku(cls, v: str) -> str:
if not v.startswith('ABC'):
raise ValueError('SKU必须以ABC开头')
return vPydantic v2 Improvements
Pydantic v2 改进点
Migration from v1
从v1迁移
python
undefinedpython
undefinedPydantic v1
Pydantic v1
class OldModel(BaseModel):
class Config:
validate_assignment = True
json_encoders = {datetime: lambda v: v.isoformat()}
class OldModel(BaseModel):
class Config:
validate_assignment = True
json_encoders = {datetime: lambda v: v.isoformat()}
Pydantic v2
Pydantic v2
class NewModel(BaseModel):
model_config = ConfigDict(
validate_assignment=True,
# json_encoders replaced by serializers
)
@model_serializer
def ser_model(self) -> dict:
return {...}class NewModel(BaseModel):
model_config = ConfigDict(
validate_assignment=True,
# json_encoders 被序列化器替代
)
@model_serializer
def ser_model(self) -> dict:
return {...}Key changes:
主要变更:
- .dict() → .model_dump()
- .dict() → .model_dump()
- .json() → .model_dump_json()
- .json() → .model_dump_json()
- .parse_obj() → .model_validate()
- .parse_obj() → .model_validate()
- .parse_raw() → .model_validate_json()
- .parse_raw() → .model_validate_json()
- @validator → @field_validator
- @validator → @field_validator
- @root_validator → @model_validator
- @root_validator → @model_validator
undefinedundefinedPerformance Improvements
性能提升
python
undefinedpython
undefinedv2 uses Rust core (pydantic-core) for 5-50x speedup
v2 使用Rust核心(pydantic-core)实现5-50倍的速度提升
from pydantic import BaseModel
import time
class Data(BaseModel):
values: list[int]
names: list[str]
from pydantic import BaseModel
import time
class Data(BaseModel):
values: list[int]
names: list[str]
Benchmark
基准测试
data = {'values': list(range(10000)), 'names': ['item'] * 10000}
start = time.perf_counter()
for _ in range(1000):
Data.model_validate(data)
elapsed = time.perf_counter() - start
print(f"Validated 1000 iterations in {elapsed:.2f}s")
undefineddata = {'values': list(range(10000)), 'names': ['item'] * 10000}
start = time.perf_counter()
for _ in range(1000):
Data.model_validate(data)
elapsed = time.perf_counter() - start
print(f"完成1000次验证耗时 {elapsed:.2f}秒")
undefinedField Types
字段类型
Built-in Types
内置类型
python
from pydantic import (
BaseModel, EmailStr, HttpUrl, UUID4,
FilePath, DirectoryPath, Json, SecretStr,
PositiveInt, NegativeFloat, conint, constr
)
from typing import Literal
from pathlib import Path
class Example(BaseModel):
# Email validation
email: EmailStr
# URL validation
website: HttpUrl
# UUID
id: UUID4
# File system paths
config_file: FilePath
data_dir: DirectoryPath
# JSON string → parsed object
metadata: Json[dict[str, str]]
# Secret (won't print in logs)
api_key: SecretStr
# Constrained types
age: PositiveInt
balance: NegativeFloat
username: constr(min_length=3, max_length=20, pattern=r'^[a-z]+$')
code: conint(ge=1000, le=9999)
# Literal types
status: Literal['pending', 'approved', 'rejected']python
from pydantic import (
BaseModel, EmailStr, HttpUrl, UUID4,
FilePath, DirectoryPath, Json, SecretStr,
PositiveInt, NegativeFloat, conint, constr
)
from typing import Literal
from pathlib import Path
class Example(BaseModel):
# 邮箱验证
email: EmailStr
# URL验证
website: HttpUrl
# UUID
id: UUID4
# 文件系统路径
config_file: FilePath
data_dir: DirectoryPath
# JSON字符串 → 解析对象
metadata: Json[dict[str, str]]
# 保密字段(日志中不会打印)
api_key: SecretStr
# 约束类型
age: PositiveInt
balance: NegativeFloat
username: constr(min_length=3, max_length=20, pattern=r'^[a-z]+$')
code: conint(ge=1000, le=9999)
# 字面量类型
status: Literal['pending', 'approved', 'rejected']Custom Types
自定义类型
python
from pydantic import GetCoreSchemaHandler, GetJsonSchemaHandler
from pydantic_core import core_schema
from typing import Any
class Color:
def __init__(self, r: int, g: int, b: int):
self.r, self.g, self.b = r, g, b
@classmethod
def __get_pydantic_core_schema__(
cls, source_type: Any, handler: GetCoreSchemaHandler
) -> core_schema.CoreSchema:
return core_schema.no_info_after_validator_function(
cls.validate,
core_schema.str_schema()
)
@classmethod
def validate(cls, v: str) -> 'Color':
if not v.startswith('#') or len(v) != 7:
raise ValueError('Invalid hex color')
r = int(v[1:3], 16)
g = int(v[3:5], 16)
b = int(v[5:7], 16)
return cls(r, g, b)
class Design(BaseModel):
primary_color: Colorpython
from pydantic import GetCoreSchemaHandler, GetJsonSchemaHandler
from pydantic_core import core_schema
from typing import Any
class Color:
def __init__(self, r: int, g: int, b: int):
self.r, self.g, self.b = r, g, b
@classmethod
def __get_pydantic_core_schema__(
cls, source_type: Any, handler: GetCoreSchemaHandler
) -> core_schema.CoreSchema:
return core_schema.no_info_after_validator_function(
cls.validate,
core_schema.str_schema()
)
@classmethod
def validate(cls, v: str) -> 'Color':
if not v.startswith('#') or len(v) != 7:
raise ValueError('无效的十六进制颜色值')
r = int(v[1:3], 16)
g = int(v[3:5], 16)
b = int(v[5:7], 16)
return cls(r, g, b)
class Design(BaseModel):
primary_color: ColorUsage
使用示例
design = Design(primary_color='#FF5733')
assert design.primary_color.r == 255
undefineddesign = Design(primary_color='#FF5733')
assert design.primary_color.r == 255
undefinedValidators
验证器
Field Validators
字段验证器
python
from pydantic import field_validator, model_validator
class Account(BaseModel):
username: str
password: str
password_confirm: str
@field_validator('username')
@classmethod
def username_alphanumeric(cls, v: str) -> str:
if not v.isalnum():
raise ValueError('must be alphanumeric')
return v
@field_validator('password')
@classmethod
def password_strong(cls, v: str) -> str:
if len(v) < 8:
raise ValueError('must be at least 8 characters')
if not any(c.isupper() for c in v):
raise ValueError('must contain uppercase letter')
return v
# Validate multiple fields
@field_validator('username', 'password')
@classmethod
def not_empty(cls, v: str) -> str:
if not v or not v.strip():
raise ValueError('must not be empty')
return v.strip()python
from pydantic import field_validator, model_validator
class Account(BaseModel):
username: str
password: str
password_confirm: str
@field_validator('username')
@classmethod
def username_alphanumeric(cls, v: str) -> str:
if not v.isalnum():
raise ValueError('必须为字母数字组合')
return v
@field_validator('password')
@classmethod
def password_strong(cls, v: str) -> str:
if len(v) < 8:
raise ValueError('长度至少为8个字符')
if not any(c.isupper() for c in v):
raise ValueError('必须包含大写字母')
return v
# 多字段验证
@field_validator('username', 'password')
@classmethod
def not_empty(cls, v: str) -> str:
if not v or not v.strip():
raise ValueError('不能为空')
return v.strip()Model Validators
模型验证器
python
from pydantic import model_validator
from typing import Self
class DateRange(BaseModel):
start_date: datetime
end_date: datetime
@model_validator(mode='after')
def check_dates(self) -> Self:
if self.end_date < self.start_date:
raise ValueError('end_date must be after start_date')
return self
class Order(BaseModel):
items: list[str]
total: float
discount: float = 0
@model_validator(mode='before')
@classmethod
def calculate_total(cls, data: dict) -> dict:
# Pre-processing before validation
if isinstance(data, dict) and 'total' not in data:
data['total'] = len(data.get('items', [])) * 10.0
return datapython
from pydantic import model_validator
from typing import Self
class DateRange(BaseModel):
start_date: datetime
end_date: datetime
@model_validator(mode='after')
def check_dates(self) -> Self:
if self.end_date < self.start_date:
raise ValueError('结束日期必须晚于开始日期')
return self
class Order(BaseModel):
items: list[str]
total: float
discount: float = 0
@model_validator(mode='before')
@classmethod
def calculate_total(cls, data: dict) -> dict:
# 验证前预处理
if isinstance(data, dict) and 'total' not in data:
data['total'] = len(data.get('items', [])) * 10.0
return dataRoot Validators (Wrap)
根验证器(包装模式)
python
from pydantic import model_validator, ValidationInfo
class Config(BaseModel):
env: Literal['dev', 'prod']
debug: bool = False
@model_validator(mode='wrap')
@classmethod
def validate_config(cls, values: Any, handler, info: ValidationInfo):
# Call default validation
result = handler(values)
# Post-validation logic
if result.env == 'prod' and result.debug:
raise ValueError('debug cannot be True in production')
return resultpython
from pydantic import model_validator, ValidationInfo
class Config(BaseModel):
env: Literal['dev', 'prod']
debug: bool = False
@model_validator(mode='wrap')
@classmethod
def validate_config(cls, values: Any, handler, info: ValidationInfo):
# 调用默认验证
result = handler(values)
# 验证后逻辑
if result.env == 'prod' and result.debug:
raise ValueError('生产环境下debug不能设为True')
return resultType Coercion and Strict Mode
类型转换与严格模式
python
from pydantic import BaseModel, ConfigDict, ValidationErrorpython
from pydantic import BaseModel, ConfigDict, ValidationErrorCoercive mode (default)
强制转换模式(默认)
class CoerciveModel(BaseModel):
count: int
price: float
data = CoerciveModel(count="42", price="19.99")
assert data.count == 42 # String → int
assert data.price == 19.99 # String → float
class CoerciveModel(BaseModel):
count: int
price: float
data = CoerciveModel(count="42", price="19.99")
assert data.count == 42 # 字符串→整数
assert data.price == 19.99 # 字符串→浮点数
Strict mode
严格模式
class StrictModel(BaseModel):
model_config = ConfigDict(strict=True)
count: int
price: floattry:
StrictModel(count="42", price="19.99") # Raises ValidationError
except ValidationError as e:
print("Strict mode: no coercion allowed")
class StrictModel(BaseModel):
model_config = ConfigDict(strict=True)
count: int
price: floattry:
StrictModel(count="42", price="19.99") # 抛出ValidationError
except ValidationError as e:
print("严格模式:不允许类型转换")
Per-field strict mode
字段级严格模式
class MixedModel(BaseModel):
flexible: int # Allows coercion
strict: Annotated[int, Field(strict=True)] # No coercion
MixedModel(flexible="1", strict=2) # OK
class MixedModel(BaseModel):
flexible: int # 允许转换
strict: Annotated[int, Field(strict=True)] # 不允许转换
MixedModel(flexible="1", strict=2) # 正常
MixedModel(flexible="1", strict="2") # ValidationError
MixedModel(flexible="1", strict="2") # 验证错误
undefinedundefinedNested Models and Recursive Types
嵌套模型与递归类型
python
from pydantic import BaseModel
from typing import ForwardRefpython
from pydantic import BaseModel
from typing import ForwardRefNested models
嵌套模型
class Address(BaseModel):
street: str
city: str
country: str
class Company(BaseModel):
name: str
address: Address
company = Company(
name="ACME Corp",
address={'street': '123 Main St', 'city': 'NYC', 'country': 'USA'}
)
class Address(BaseModel):
street: str
city: str
country: str
class Company(BaseModel):
name: str
address: Address
company = Company(
name="ACME Corp",
address={'street': '123 Main St', 'city': 'NYC', 'country': 'USA'}
)
Recursive types (tree structure)
递归类型(树形结构)
class TreeNode(BaseModel):
value: int
children: list['TreeNode'] = []
TreeNode.model_rebuild() # Required for forward references
tree = TreeNode(
value=1,
children=[
TreeNode(value=2, children=[TreeNode(value=4)]),
TreeNode(value=3)
]
)
class TreeNode(BaseModel):
value: int
children: list['TreeNode'] = []
TreeNode.model_rebuild() # 前向引用需要调用此方法
tree = TreeNode(
value=1,
children=[
TreeNode(value=2, children=[TreeNode(value=4)]),
TreeNode(value=3)
]
)
Self-referencing with ForwardRef
使用ForwardRef实现自引用
class Category(BaseModel):
name: str
parent: 'Category | None' = None
subcategories: list['Category'] = []
Category.model_rebuild()
undefinedclass Category(BaseModel):
name: str
parent: 'Category | None' = None
subcategories: list['Category'] = []
Category.model_rebuild()
undefinedGeneric Models
泛型模型
python
from pydantic import BaseModel
from typing import Generic, TypeVar
T = TypeVar('T')
class Response(BaseModel, Generic[T]):
success: bool
data: T
message: str = ''
class User(BaseModel):
id: int
name: strpython
from pydantic import BaseModel
from typing import Generic, TypeVar
T = TypeVar('T')
class Response(BaseModel, Generic[T]):
success: bool
data: T
message: str = ''
class User(BaseModel):
id: int
name: strUsage with concrete type
使用具体类型
user_response = Response[User](
success=True,
data=User(id=1, name='Alice')
)
user_response = Response[User](
success=True,
data=User(id=1, name='Alice')
)
List response
列表响应
list_response = Response[list[User]](
success=True,
data=[User(id=1, name='Alice'), User(id=2, name='Bob')]
)
list_response = Response[list[User]](
success=True,
data=[User(id=1, name='Alice'), User(id=2, name='Bob')]
)
Generic repository pattern
泛型仓库模式
class Repository(BaseModel, Generic[T]):
items: list[T]
def add(self, item: T) -> None:
self.items.append(item)user_repo = RepositoryUser
user_repo.add(User(id=1, name='Alice'))
undefinedclass Repository(BaseModel, Generic[T]):
items: list[T]
def add(self, item: T) -> None:
self.items.append(item)user_repo = RepositoryUser
user_repo.add(User(id=1, name='Alice'))
undefinedSerialization
序列化
Model Dump
模型转字典
python
from pydantic import BaseModel, Field, field_serializer
class Article(BaseModel):
title: str
content: str
tags: list[str]
metadata: dict[str, Any] = {}
# Serialization customization
@field_serializer('tags')
def serialize_tags(self, tags: list[str]) -> str:
return ','.join(tags)
article = Article(
title='Pydantic Guide',
content='...',
tags=['python', 'validation']
)python
from pydantic import BaseModel, Field, field_serializer
class Article(BaseModel):
title: str
content: str
tags: list[str]
metadata: dict[str, Any] = {}
# 自定义序列化
@field_serializer('tags')
def serialize_tags(self, tags: list[str]) -> str:
return ','.join(tags)
article = Article(
title='Pydantic指南',
content='...',
tags=['python', 'validation']
)Dump to dict
转换为字典
data = article.model_dump()
data = article.model_dump()
{'title': 'Pydantic Guide', 'tags': 'python,validation', ...}
{'title': 'Pydantic指南', 'tags': 'python,validation', ...}
Exclude fields
排除指定字段
data = article.model_dump(exclude={'metadata'})
data = article.model_dump(exclude={'metadata'})
Include only specific fields
仅包含指定字段
data = article.model_dump(include={'title', 'tags'})
data = article.model_dump(include={'title', 'tags'})
Exclude unset fields
排除未设置的字段
article2 = Article(title='Test', content='...', tags=[])
data = article2.model_dump(exclude_unset=True) # metadata excluded
article2 = Article(title='测试', content='...', tags=[])
data = article2.model_dump(exclude_unset=True) # metadata被排除
By alias
使用别名
class AliasModel(BaseModel):
internal_name: str = Field(alias='externalName')
model = AliasModel(externalName='value')
model.model_dump(by_alias=True) # {'externalName': 'value'}
undefinedclass AliasModel(BaseModel):
internal_name: str = Field(alias='externalName')
model = AliasModel(externalName='value')
model.model_dump(by_alias=True) # {'externalName': 'value'}
undefinedJSON Serialization
JSON序列化
python
from datetime import datetime
from pydantic import BaseModel, field_serializer
class Event(BaseModel):
name: str
timestamp: datetime
@field_serializer('timestamp')
def serialize_dt(self, dt: datetime) -> str:
return dt.isoformat()
event = Event(name='Deploy', timestamp=datetime.now())python
from datetime import datetime
from pydantic import BaseModel, field_serializer
class Event(BaseModel):
name: str
timestamp: datetime
@field_serializer('timestamp')
def serialize_dt(self, dt: datetime) -> str:
return dt.isoformat()
event = Event(name='部署', timestamp=datetime.now())Dump to JSON string
转换为JSON字符串
json_str = event.model_dump_json()
json_str = event.model_dump_json()
'{"name":"Deploy","timestamp":"2025-11-30T..."}'
'{"name":"部署","timestamp":"2025-11-30T..."}'
Pretty print
格式化输出
json_str = event.model_dump_json(indent=2)
json_str = event.model_dump_json(indent=2)
Parse from JSON
从JSON解析
event2 = Event.model_validate_json(json_str)
undefinedevent2 = Event.model_validate_json(json_str)
undefinedCustom Serializers
自定义序列化器
python
from pydantic import model_serializer
class User(BaseModel):
id: int
username: str
password: SecretStr
@model_serializer
def ser_model(self) -> dict[str, Any]:
return {
'id': self.id,
'username': self.username,
# Never serialize password
}
user = User(id=1, username='alice', password='secret123')
assert 'password' not in user.model_dump()python
from pydantic import model_serializer
class User(BaseModel):
id: int
username: str
password: SecretStr
@model_serializer
def ser_model(self) -> dict[str, Any]:
return {
'id': self.id,
'username': self.username,
# 密码永不序列化
}
user = User(id=1, username='alice', password='secret123')
assert 'password' not in user.model_dump()Settings Management
配置管理
BaseSettings
BaseSettings
python
from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import Field
class AppSettings(BaseSettings):
model_config = SettingsConfigDict(
env_file='.env',
env_file_encoding='utf-8',
env_prefix='APP_',
case_sensitive=False
)
# Environment variables
database_url: str
redis_url: str = 'redis://localhost:6379'
secret_key: SecretStr
debug: bool = False
# Nested settings
class SMTPSettings(BaseModel):
host: str
port: int = 587
username: str
password: SecretStr
smtp: SMTPSettingspython
from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import Field
class AppSettings(BaseSettings):
model_config = SettingsConfigDict(
env_file='.env',
env_file_encoding='utf-8',
env_prefix='APP_',
case_sensitive=False
)
# 环境变量
database_url: str
redis_url: str = 'redis://localhost:6379'
secret_key: SecretStr
debug: bool = False
# 嵌套配置
class SMTPSettings(BaseModel):
host: str
port: int = 587
username: str
password: SecretStr
smtp: SMTPSettingsReads from environment variables:
从环境变量读取:
APP_DATABASE_URL, APP_REDIS_URL, APP_SECRET_KEY, APP_DEBUG
APP_DATABASE_URL, APP_REDIS_URL, APP_SECRET_KEY, APP_DEBUG
APP_SMTP__HOST, APP_SMTP__PORT, etc.
APP_SMTP__HOST, APP_SMTP__PORT, 等
settings = AppSettings()
undefinedsettings = AppSettings()
undefinedMulti-Environment Settings
多环境配置
python
from functools import lru_cache
class Settings(BaseSettings):
environment: Literal['dev', 'staging', 'prod'] = 'dev'
database_url: str
api_key: SecretStr
model_config = SettingsConfigDict(
env_file='.env',
extra='ignore'
)
@property
def is_production(self) -> bool:
return self.environment == 'prod'
@lru_cache
def get_settings() -> Settings:
return Settings()python
from functools import lru_cache
class Settings(BaseSettings):
environment: Literal['dev', 'staging', 'prod'] = 'dev'
database_url: str
api_key: SecretStr
model_config = SettingsConfigDict(
env_file='.env',
extra='ignore'
)
@property
def is_production(self) -> bool:
return self.environment == 'prod'
@lru_cache
def get_settings() -> Settings:
return Settings()Usage in FastAPI
在FastAPI中使用
from fastapi import Depends
@app.get('/config')
def get_config(settings: Settings = Depends(get_settings)):
return {'env': settings.environment}
undefinedfrom fastapi import Depends
@app.get('/config')
def get_config(settings: Settings = Depends(get_settings)):
return {'env': settings.environment}
undefinedFastAPI Integration
FastAPI集成
Request/Response Models
请求/响应模型
python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserCreate(BaseModel):
username: str = Field(min_length=3, max_length=50)
email: EmailStr
password: str = Field(min_length=8)
class UserResponse(BaseModel):
id: int
username: str
email: EmailStr
model_config = ConfigDict(from_attributes=True)
@app.post('/users', response_model=UserResponse)
def create_user(user: UserCreate):
# FastAPI auto-validates request body
# Returns only fields in UserResponse (password excluded)
return UserResponse(
id=1,
username=user.username,
email=user.email
)python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserCreate(BaseModel):
username: str = Field(min_length=3, max_length=50)
email: EmailStr
password: str = Field(min_length=8)
class UserResponse(BaseModel):
id: int
username: str
email: EmailStr
model_config = ConfigDict(from_attributes=True)
@app.post('/users', response_model=UserResponse)
def create_user(user: UserCreate):
# FastAPI自动验证请求体
# 仅返回UserResponse中定义的字段(排除密码)
return UserResponse(
id=1,
username=user.username,
email=user.email
)Query Parameters
查询参数
python
from pydantic import BaseModel, Field
from fastapi import Query
class PaginationParams(BaseModel):
skip: int = Field(0, ge=0)
limit: int = Field(10, ge=1, le=100)
class SearchParams(BaseModel):
q: str = Field(..., min_length=1)
category: str | None = None
sort_by: Literal['date', 'relevance'] = 'relevance'
@app.get('/search')
def search(params: SearchParams = Query()):
return {'query': params.q, 'sort': params.sort_by}python
from pydantic import BaseModel, Field
from fastapi import Query
class PaginationParams(BaseModel):
skip: int = Field(0, ge=0)
limit: int = Field(10, ge=1, le=100)
class SearchParams(BaseModel):
q: str = Field(..., min_length=1)
category: str | None = None
sort_by: Literal['date', 'relevance'] = 'relevance'
@app.get('/search')
def search(params: SearchParams = Query()):
return {'query': params.q, 'sort': params.sort_by}Response Model Customization
响应模型自定义
python
class DetailedUser(BaseModel):
id: int
username: str
email: EmailStr
created_at: datetime
last_login: datetime | None
@app.get('/users/{user_id}', response_model=DetailedUser)
def get_user(user_id: int, include_dates: bool = False):
user = DetailedUser(
id=user_id,
username='alice',
email='alice@example.com',
created_at=datetime.now(),
last_login=None
)
if not include_dates:
return user.model_dump(exclude={'created_at', 'last_login'})
return userpython
class DetailedUser(BaseModel):
id: int
username: str
email: EmailStr
created_at: datetime
last_login: datetime | None
@app.get('/users/{user_id}', response_model=DetailedUser)
def get_user(user_id: int, include_dates: bool = False):
user = DetailedUser(
id=user_id,
username='alice',
email='alice@example.com',
created_at=datetime.now(),
last_login=None
)
if not include_dates:
return user.model_dump(exclude={'created_at', 'last_login'})
return userSQLAlchemy Integration
SQLAlchemy集成
ORM Models with Pydantic
ORM模型与Pydantic
python
from sqlalchemy import Column, Integer, String, DateTime
from sqlalchemy.orm import DeclarativeBase
from pydantic import BaseModel, ConfigDict
class Base(DeclarativeBase):
passpython
from sqlalchemy import Column, Integer, String, DateTime
from sqlalchemy.orm import DeclarativeBase
from pydantic import BaseModel, ConfigDict
class Base(DeclarativeBase):
passSQLAlchemy ORM model
SQLAlchemy ORM模型
class UserDB(Base):
tablename = 'users'
id = Column(Integer, primary_key=True)
username = Column(String(50), unique=True)
email = Column(String(100))
created_at = Column(DateTime, default=datetime.utcnow)class UserDB(Base):
tablename = 'users'
id = Column(Integer, primary_key=True)
username = Column(String(50), unique=True)
email = Column(String(100))
created_at = Column(DateTime, default=datetime.utcnow)Pydantic model for validation
Pydantic验证模型
class UserSchema(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
username: str
email: EmailStr
created_at: datetimeclass UserSchema(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
username: str
email: EmailStr
created_at: datetimeUsage
使用示例
from sqlalchemy.orm import Session
def get_user(db: Session, user_id: int) -> UserSchema:
user = db.query(UserDB).filter(UserDB.id == user_id).first()
return UserSchema.model_validate(user) # ORM → Pydantic
undefinedfrom sqlalchemy.orm import Session
def get_user(db: Session, user_id: int) -> UserSchema:
user = db.query(UserDB).filter(UserDB.id == user_id).first()
return UserSchema.model_validate(user) # ORM → Pydantic
undefinedHybrid Approach
混合方案
python
from pydantic import BaseModel
class UserBase(BaseModel):
username: str
email: EmailStr
class UserCreate(UserBase):
password: str
class UserUpdate(BaseModel):
username: str | None = None
email: EmailStr | None = None
password: str | None = None
class UserInDB(UserBase):
model_config = ConfigDict(from_attributes=True)
id: int
created_at: datetime
password_hash: strpython
from pydantic import BaseModel
class UserBase(BaseModel):
username: str
email: EmailStr
class UserCreate(UserBase):
password: str
class UserUpdate(BaseModel):
username: str | None = None
email: EmailStr | None = None
password: str | None = None
class UserInDB(UserBase):
model_config = ConfigDict(from_attributes=True)
id: int
created_at: datetime
password_hash: strCRUD operations
CRUD操作
def create_user(db: Session, user: UserCreate) -> UserInDB:
db_user = UserDB(
username=user.username,
email=user.email,
password_hash=hash_password(user.password)
)
db.add(db_user)
db.commit()
db.refresh(db_user)
return UserInDB.model_validate(db_user)
undefineddef create_user(db: Session, user: UserCreate) -> UserInDB:
db_user = UserDB(
username=user.username,
email=user.email,
password_hash=hash_password(user.password)
)
db.add(db_user)
db.commit()
db.refresh(db_user)
return UserInDB.model_validate(db_user)
undefinedDjango Integration
Django集成
Django Model Validation
Django模型验证
python
from django.db import models
from pydantic import BaseModel, field_validatorpython
from django.db import models
from pydantic import BaseModel, field_validatorDjango model
Django模型
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
published = models.BooleanField(default=False)
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
published = models.BooleanField(default=False)
Pydantic schema
Pydantic schema
class ArticleSchema(BaseModel):
model_config = ConfigDict(from_attributes=True)
title: str = Field(max_length=200)
content: str
published: bool = False
@field_validator('content')
@classmethod
def validate_content(cls, v: str) -> str:
if len(v) < 100:
raise ValueError('Content too short')
return vclass ArticleSchema(BaseModel):
model_config = ConfigDict(from_attributes=True)
title: str = Field(max_length=200)
content: str
published: bool = False
@field_validator('content')
@classmethod
def validate_content(cls, v: str) -> str:
if len(v) < 100:
raise ValueError('内容过短')
return vUsage in Django views
在Django视图中使用
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
@require_http_methods(['POST'])
def create_article(request):
try:
data = ArticleSchema.model_validate_json(request.body)
article = Article.objects.create(**data.model_dump())
return JsonResponse({'id': article.id})
except ValidationError as e:
return JsonResponse({'errors': e.errors()}, status=400)
undefinedfrom django.http import JsonResponse
from django.views.decorators.http import require_http_methods
@require_http_methods(['POST'])
def create_article(request):
try:
data = ArticleSchema.model_validate_json(request.body)
article = Article.objects.create(**data.model_dump())
return JsonResponse({'id': article.id})
except ValidationError as e:
return JsonResponse({'errors': e.errors()}, status=400)
undefinedComputed Fields
计算字段
python
from pydantic import computed_field
class Rectangle(BaseModel):
width: float
height: float
@computed_field
@property
def area(self) -> float:
return self.width * self.height
@computed_field
@property
def perimeter(self) -> float:
return 2 * (self.width + self.height)
rect = Rectangle(width=10, height=5)
assert rect.area == 50
assert rect.perimeter == 30python
from pydantic import computed_field
class Rectangle(BaseModel):
width: float
height: float
@computed_field
@property
def area(self) -> float:
return self.width * self.height
@computed_field
@property
def perimeter(self) -> float:
return 2 * (self.width + self.height)
rect = Rectangle(width=10, height=5)
assert rect.area == 50
assert rect.perimeter == 30Computed fields in serialization
计算字段会被序列化
data = rect.model_dump()
data = rect.model_dump()
{'width': 10.0, 'height': 5.0, 'area': 50.0, 'perimeter': 30.0}
{'width': 10.0, 'height': 5.0, 'area': 50.0, 'perimeter': 30.0}
undefinedundefinedCustom Errors
自定义错误
python
from pydantic import BaseModel, field_validator, ValidationError
from pydantic_core import PydanticCustomError
class StrictUser(BaseModel):
username: str
age: int
@field_validator('username')
@classmethod
def validate_username(cls, v: str) -> str:
if len(v) < 3:
raise PydanticCustomError(
'username_too_short',
'Username must be at least 3 characters',
{'min_length': 3, 'actual_length': len(v)}
)
return v
@field_validator('age')
@classmethod
def validate_age(cls, v: int) -> int:
if v < 18:
raise PydanticCustomError(
'underage',
'User must be at least 18 years old',
{'age': v, 'minimum_age': 18}
)
return vpython
from pydantic import BaseModel, field_validator, ValidationError
from pydantic_core import PydanticCustomError
class StrictUser(BaseModel):
username: str
age: int
@field_validator('username')
@classmethod
def validate_username(cls, v: str) -> str:
if len(v) < 3:
raise PydanticCustomError(
'username_too_short',
'用户名长度至少为3个字符',
{'min_length': 3, 'actual_length': len(v)}
)
return v
@field_validator('age')
@classmethod
def validate_age(cls, v: int) -> int:
if v < 18:
raise PydanticCustomError(
'underage',
'用户必须年满18岁',
{'age': v, 'minimum_age': 18}
)
return vCustom error handling
自定义错误处理
try:
StrictUser(username='ab', age=16)
except ValidationError as e:
for error in e.errors():
print(f"{error['type']}: {error['msg']}")
print(f"Context: {error.get('ctx')}")
undefinedtry:
StrictUser(username='ab', age=16)
except ValidationError as e:
for error in e.errors():
print(f"{error['type']}: {error['msg']}")
print(f"上下文: {error.get('ctx')}")
undefinedPerformance Optimization
性能优化
V2 Rust Core Benefits
V2 Rust核心优势
python
undefinedpython
undefinedPydantic v2 uses pydantic-core (Rust) for:
Pydantic v2 使用pydantic-core(Rust)实现:
- 5-50x faster validation
- 5-50倍更快的验证速度
- Lower memory usage
- 更低的内存占用
- Better error messages
- 更友好的错误提示
- Improved JSON parsing
- 改进的JSON解析
import timeit
from pydantic import BaseModel
class Data(BaseModel):
values: list[int]
names: list[str]
metadata: dict[str, Any]
import timeit
from pydantic import BaseModel
class Data(BaseModel):
values: list[int]
names: list[str]
metadata: dict[str, Any]
Benchmark
基准测试
data_dict = {
'values': list(range(1000)),
'names': ['item'] * 1000,
'metadata': {'key': 'value'}
}
def validate():
Data.model_validate(data_dict)
time_taken = timeit.timeit(validate, number=10000)
print(f"10000 validations: {time_taken:.2f}s")
undefineddata_dict = {
'values': list(range(1000)),
'names': ['item'] * 1000,
'metadata': {'key': 'value'}
}
def validate():
Data.model_validate(data_dict)
time_taken = timeit.timeit(validate, number=10000)
print(f"10000次验证耗时: {time_taken:.2f}秒")
undefinedOptimization Techniques
优化技巧
python
from pydantic import BaseModel, ConfigDict
class OptimizedModel(BaseModel):
model_config = ConfigDict(
# Validate assignment only when needed
validate_assignment=False,
# Disable validation for internal use
validate_default=False,
# Use slots for memory efficiency
# (Not available in Pydantic v2 BaseModel directly)
)
data: list[int]python
from pydantic import BaseModel, ConfigDict
class OptimizedModel(BaseModel):
model_config = ConfigDict(
# 仅在需要时验证赋值
validate_assignment=False,
# 禁用默认值验证
validate_default=False,
# 使用slots提升内存效率
# (Pydantic v2 BaseModel暂不直接支持)
)
data: list[int]Reuse validators
复用验证器
from functools import lru_cache
@lru_cache(maxsize=128)
def get_validator(model_class):
return model_class.model_validate
from functools import lru_cache
@lru_cache(maxsize=128)
def get_validator(model_class):
return model_class.model_validate
Bulk validation
批量验证
def validate_bulk(items: list[dict]) -> list[Data]:
validator = get_validator(Data)
return [validator(item) for item in items]
undefineddef validate_bulk(items: list[dict]) -> list[Data]:
validator = get_validator(Data)
return [validator(item) for item in items]
undefinedJSON Schema Generation
JSON Schema生成
python
from pydantic import BaseModel, Field
class Product(BaseModel):
"""Product model for catalog"""
id: int = Field(description="Unique product identifier")
name: str = Field(description="Product name", examples=["Widget"])
price: float = Field(gt=0, description="Price in USD")
tags: list[str] = Field(default=[], description="Product tags")python
from pydantic import BaseModel, Field
class Product(BaseModel):
"""产品目录模型"""
id: int = Field(description="唯一产品ID")
name: str = Field(description="产品名称", examples=["小部件"])
price: float = Field(gt=0, description="美元价格")
tags: list[str] = Field(default=[], description="产品标签")Generate JSON Schema
生成JSON Schema
schema = Product.model_json_schema()
print(json.dumps(schema, indent=2))
schema = Product.model_json_schema()
print(json.dumps(schema, indent=2))
{
{
"title": "Product",
"title": "Product",
"description": "Product model for catalog",
"description": "产品目录模型",
"type": "object",
"type": "object",
"properties": {
"properties": {
"id": {"type": "integer", "description": "Unique product identifier"},
"id": {"type": "integer", "description": "唯一产品ID"},
"name": {"type": "string", "description": "Product name"},
"name": {"type": "string", "description": "产品名称"},
...
...
},
},
"required": ["id", "name", "price"]
"required": ["id", "name", "price"]
}
}
OpenAPI compatible
兼容OpenAPI
from fastapi import FastAPI
app = FastAPI()
@app.post('/products')
def create_product(product: Product):
return product
from fastapi import FastAPI
app = FastAPI()
@app.post('/products')
def create_product(product: Product):
return product
FastAPI auto-generates OpenAPI schema from Pydantic models
FastAPI自动从Pydantic模型生成OpenAPI Schema
undefinedundefinedDataclass Integration
数据类集成
python
from pydantic.dataclasses import dataclass
from pydantic import Field
@dataclass
class User:
id: int
name: str = Field(min_length=1)
email: str = Field(pattern=r'.+@.+\..+')python
from pydantic.dataclasses import dataclass
from pydantic import Field
@dataclass
class User:
id: int
name: str = Field(min_length=1)
email: str = Field(pattern=r'.+@.+\..+')Works like Pydantic BaseModel with validation
具备Pydantic BaseModel的验证能力
user = User(id=1, name='Alice', email='alice@example.com')
user = User(id=1, name='Alice', email='alice@example.com')
Validation on construction
实例化时验证
try:
User(id=2, name='', email='invalid')
except ValidationError as e:
print(e.errors())
try:
User(id=2, name='', email='invalid')
except ValidationError as e:
print(e.errors())
Convert to Pydantic BaseModel
转换为Pydantic BaseModel
from pydantic import BaseModel
class UserModel(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
name: str
email: struser_model = UserModel.model_validate(user)
undefinedfrom pydantic import BaseModel
class UserModel(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
name: str
email: struser_model = UserModel.model_validate(user)
undefinedTesting Strategies
测试策略
Unit Testing Models
模型单元测试
python
import pytest
from pydantic import ValidationError
def test_user_validation():
# Valid data
user = User(id=1, name='Alice', email='alice@example.com')
assert user.name == 'Alice'
# Invalid data
with pytest.raises(ValidationError) as exc_info:
User(id='invalid', name='Bob', email='bob@example.com')
errors = exc_info.value.errors()
assert errors[0]['type'] == 'int_parsing'
def test_user_serialization():
user = User(id=1, name='Alice', email='alice@example.com')
data = user.model_dump()
assert data == {
'id': 1,
'name': 'Alice',
'email': 'alice@example.com'
}
def test_nested_validation():
company = Company(
name='ACME',
address={'street': '123 Main', 'city': 'NYC', 'country': 'USA'}
)
assert company.address.city == 'NYC'python
import pytest
from pydantic import ValidationError
def test_user_validation():
# 有效数据
user = User(id=1, name='Alice', email='alice@example.com')
assert user.name == 'Alice'
# 无效数据
with pytest.raises(ValidationError) as exc_info:
User(id='invalid', name='Bob', email='bob@example.com')
errors = exc_info.value.errors()
assert errors[0]['type'] == 'int_parsing'
def test_user_serialization():
user = User(id=1, name='Alice', email='alice@example.com')
data = user.model_dump()
assert data == {
'id': 1,
'name': 'Alice',
'email': 'alice@example.com'
}
def test_nested_validation():
company = Company(
name='ACME',
address={'street': '123 Main', 'city': 'NYC', 'country': 'USA'}
)
assert company.address.city == 'NYC'Testing with Fixtures
测试夹具使用
python
@pytest.fixture
def sample_user_data():
return {
'id': 1,
'name': 'Alice',
'email': 'alice@example.com'
}
@pytest.fixture
def sample_user(sample_user_data):
return User(**sample_user_data)
def test_with_fixtures(sample_user):
assert sample_user.name == 'Alice'
def test_invalid_email(sample_user_data):
sample_user_data['email'] = 'invalid'
with pytest.raises(ValidationError):
User(**sample_user_data)python
@pytest.fixture
def sample_user_data():
return {
'id': 1,
'name': 'Alice',
'email': 'alice@example.com'
}
@pytest.fixture
def sample_user(sample_user_data):
return User(**sample_user_data)
def test_with_fixtures(sample_user):
assert sample_user.name == 'Alice'
def test_invalid_email(sample_user_data):
sample_user_data['email'] = 'invalid'
with pytest.raises(ValidationError):
User(**sample_user_data)Property-Based Testing
属性化测试
python
from hypothesis import given, strategies as st
@given(
id=st.integers(min_value=1),
name=st.text(min_size=1, max_size=100),
email=st.emails()
)
def test_user_always_valid(id, name, email):
user = User(id=id, name=name, email=email)
assert user.id == id
assert user.name == name
assert user.email == emailpython
from hypothesis import given, strategies as st
@given(
id=st.integers(min_value=1),
name=st.text(min_size=1, max_size=100),
email=st.emails()
)
def test_user_always_valid(id, name, email):
user = User(id=id, name=name, email=email)
assert user.id == id
assert user.name == name
assert user.email == emailMigration Guide (v1 → v2)
迁移指南(v1 → v2)
Key Changes
主要变更
python
undefinedpython
undefinedv1
v1
from pydantic import BaseModel
class OldModel(BaseModel):
class Config:
validate_assignment = True
arbitrary_types_allowed = True
# Validators
@validator('field')
def validate_field(cls, v):
return v
@root_validator
def validate_model(cls, values):
return values
# Serialization
data = model.dict()
json_str = model.json()
# Parsing
model = OldModel.parse_obj(data)
model = OldModel.parse_raw(json_str)from pydantic import BaseModel
class OldModel(BaseModel):
class Config:
validate_assignment = True
arbitrary_types_allowed = True
# 验证器
@validator('field')
def validate_field(cls, v):
return v
@root_validator
def validate_model(cls, values):
return values
# 序列化
data = model.dict()
json_str = model.json()
# 解析
model = OldModel.parse_obj(data)
model = OldModel.parse_raw(json_str)v2
v2
from pydantic import BaseModel, ConfigDict, field_validator, model_validator
class NewModel(BaseModel):
model_config = ConfigDict(
validate_assignment=True,
arbitrary_types_allowed=True
)
# Field validators
@field_validator('field')
@classmethod
def validate_field(cls, v):
return v
# Model validators
@model_validator(mode='after')
def validate_model(self):
return self
# Serialization
data = model.model_dump()
json_str = model.model_dump_json()
# Parsing
model = NewModel.model_validate(data)
model = NewModel.model_validate_json(json_str)undefinedfrom pydantic import BaseModel, ConfigDict, field_validator, model_validator
class NewModel(BaseModel):
model_config = ConfigDict(
validate_assignment=True,
arbitrary_types_allowed=True
)
# 字段验证器
@field_validator('field')
@classmethod
def validate_field(cls, v):
return v
# 模型验证器
@model_validator(mode='after')
def validate_model(self):
return self
# 序列化
data = model.model_dump()
json_str = model.model_dump_json()
# 解析
model = NewModel.model_validate(data)
model = NewModel.model_validate_json(json_str)undefinedMigration Checklist
迁移检查清单
- Replace with
class Configmodel_config = ConfigDict() - Update →
.dict().model_dump() - Update →
.json().model_dump_json() - Update →
.parse_obj().model_validate() - Update →
.parse_raw().model_validate_json() - Update →
@validatorwith@field_validator@classmethod - Update →
@root_validator@model_validator(mode='after') - Review → use
json_encoders@field_serializer - Test strict mode behavior changes
- Update custom types to use
__get_pydantic_core_schema__
- 将替换为
class Configmodel_config = ConfigDict() - 更新→
.dict().model_dump() - 更新→
.json().model_dump_json() - 更新→
.parse_obj().model_validate() - 更新→
.parse_raw().model_validate_json() - 更新→ 带
@validator的@classmethod@field_validator - 更新→
@root_validator@model_validator(mode='after') - 替换→ 使用
json_encoders@field_serializer - 测试严格模式行为变更
- 更新自定义类型以使用
__get_pydantic_core_schema__
Best Practices
最佳实践
Model Organization
模型组织
python
undefinedpython
undefinedSeparate schemas by use case
按使用场景拆分Schema
class UserBase(BaseModel):
"""Shared fields"""
username: str
email: EmailStr
class UserCreate(UserBase):
"""API request for creating user"""
password: str
class UserUpdate(BaseModel):
"""API request for updating user (all optional)"""
username: str | None = None
email: EmailStr | None = None
password: str | None = None
class UserInDB(UserBase):
"""Database representation"""
model_config = ConfigDict(from_attributes=True)
id: int
password_hash: str
created_at: datetimeclass UserResponse(UserBase):
"""API response (excludes sensitive data)"""
id: int
created_at: datetime
undefinedclass UserBase(BaseModel):
"""共享字段"""
username: str
email: EmailStr
class UserCreate(UserBase):
"""创建用户的API请求模型"""
password: str
class UserUpdate(BaseModel):
"""更新用户的API请求模型(所有字段可选)"""
username: str | None = None
email: EmailStr | None = None
password: str | None = None
class UserInDB(UserBase):
"""数据库表示模型"""
model_config = ConfigDict(from_attributes=True)
id: int
password_hash: str
created_at: datetimeclass UserResponse(UserBase):
"""API响应模型(排除敏感数据)"""
id: int
created_at: datetime
undefinedValidation Best Practices
验证最佳实践
python
undefinedpython
undefinedUse Field for constraints, not validators
使用Field定义约束,而非验证器
class Good(BaseModel):
age: int = Field(ge=0, le=150)
email: EmailStr
class Bad(BaseModel):
age: int
email: str
@field_validator('age')
@classmethod
def validate_age(cls, v):
if v < 0 or v > 150:
raise ValueError('invalid age')
return vclass Good(BaseModel):
age: int = Field(ge=0, le=150)
email: EmailStr
class Bad(BaseModel):
age: int
email: str
@field_validator('age')
@classmethod
def validate_age(cls, v):
if v < 0 or v > 150:
raise ValueError('无效年龄')
return vPrefer composition over inheritance
优先组合而非继承
class TimestampMixin(BaseModel):
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow)
class User(TimestampMixin):
username: str
email: EmailStr
undefinedclass TimestampMixin(BaseModel):
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow)
class User(TimestampMixin):
username: str
email: EmailStr
undefinedError Handling
错误处理
python
from pydantic import ValidationError
def safe_validate(data: dict) -> User | None:
try:
return User.model_validate(data)
except ValidationError as e:
# Log validation errors
logger.error(f"Validation failed: {e.errors()}")
return None
def validate_with_details(data: dict):
try:
return User.model_validate(data)
except ValidationError as e:
# Return user-friendly errors
return {
'success': False,
'errors': [
{
'field': '.'.join(str(loc) for loc in err['loc']),
'message': err['msg'],
'type': err['type']
}
for err in e.errors()
]
}python
from pydantic import ValidationError
def safe_validate(data: dict) -> User | None:
try:
return User.model_validate(data)
except ValidationError as e:
# 记录验证错误
logger.error(f"验证失败: {e.errors()}")
return None
def validate_with_details(data: dict):
try:
return User.model_validate(data)
except ValidationError as e:
# 返回用户友好的错误信息
return {
'success': False,
'errors': [
{
'field': '.'.join(str(loc) for loc in err['loc']),
'message': err['msg'],
'type': err['type']
}
for err in e.errors()
]
}Common Patterns
常见模式
API Response Wrapper
API响应包装器
python
from typing import Generic, TypeVar
T = TypeVar('T')
class APIResponse(BaseModel, Generic[T]):
success: bool
data: T | None = None
error: str | None = None
metadata: dict[str, Any] = {}python
from typing import Generic, TypeVar
T = TypeVar('T')
class APIResponse(BaseModel, Generic[T]):
success: bool
data: T | None = None
error: str | None = None
metadata: dict[str, Any] = {}Usage
使用示例
user_response = APIResponse[User](
success=True,
data=User(id=1, name='Alice', email='alice@example.com')
)
error_response = APIResponse[User](
success=False,
error='User not found'
)
undefineduser_response = APIResponse[User](
success=True,
data=User(id=1, name='Alice', email='alice@example.com')
)
error_response = APIResponse[User](
success=False,
error='用户不存在'
)
undefinedPagination
分页
python
class PaginatedResponse(BaseModel, Generic[T]):
items: list[T]
total: int
page: int
page_size: int
@computed_field
@property
def total_pages(self) -> int:
return (self.total + self.page_size - 1) // self.page_size
users = PaginatedResponse[User](
items=[...],
total=100,
page=1,
page_size=10
)
assert users.total_pages == 10python
class PaginatedResponse(BaseModel, Generic[T]):
items: list[T]
total: int
page: int
page_size: int
@computed_field
@property
def total_pages(self) -> int:
return (self.total + self.page_size - 1) // self.page_size
users = PaginatedResponse[User](
items=[...],
total=100,
page=1,
page_size=10
)
assert users.total_pages == 10Audit Fields
审计字段
python
class AuditMixin(BaseModel):
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow)
created_by: int | None = None
updated_by: int | None = None
class Document(AuditMixin):
title: str
content: str
@model_validator(mode='before')
@classmethod
def update_timestamp(cls, data: dict) -> dict:
if isinstance(data, dict):
data['updated_at'] = datetime.utcnow()
return datapython
class AuditMixin(BaseModel):
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow)
created_by: int | None = None
updated_by: int | None = None
class Document(AuditMixin):
title: str
content: str
@model_validator(mode='before')
@classmethod
def update_timestamp(cls, data: dict) -> dict:
if isinstance(data, dict):
data['updated_at'] = datetime.utcnow()
return dataRelated Skills
相关技能
When using Pydantic, consider these complementary skills:
- fastapi-local-dev: FastAPI development server patterns with Pydantic integration
- sqlalchemy: SQLAlchemy ORM patterns for database models with Pydantic validation
- django: Django framework integration with Pydantic schemas
- pytest: Testing strategies for Pydantic models and validation
使用Pydantic时,可搭配以下互补技能:
- fastapi-local-dev: 带Pydantic集成的FastAPI开发服务器模式
- sqlalchemy: 带Pydantic验证的SQLAlchemy ORM模型模式
- django: 带Pydantic Schema的Django框架集成
- pytest: Pydantic模型与验证的测试策略
Quick FastAPI Integration Reference (Inlined for Standalone Use)
快速FastAPI集成参考(独立使用)
python
undefinedpython
undefinedFastAPI with Pydantic (basic pattern)
FastAPI与Pydantic(基础模式)
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserCreate(BaseModel):
username: str
email: EmailStr
password: str
class UserResponse(BaseModel):
id: int
username: str
email: EmailStr
model_config = ConfigDict(from_attributes=True)@app.post('/users', response_model=UserResponse)
def create_user(user: UserCreate):
# FastAPI auto-validates using Pydantic
# response_model filters out password
return UserResponse(id=1, username=user.username, email=user.email)
undefinedfrom fastapi import FastAPI, HTTPException
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserCreate(BaseModel):
username: str
email: EmailStr
password: str
class UserResponse(BaseModel):
id: int
username: str
email: EmailStr
model_config = ConfigDict(from_attributes=True)@app.post('/users', response_model=UserResponse)
def create_user(user: UserCreate):
# FastAPI通过Pydantic自动验证
# response_model过滤掉密码字段
return UserResponse(id=1, username=user.username, email=user.email)
undefinedQuick SQLAlchemy Integration Reference (Inlined for Standalone Use)
快速SQLAlchemy集成参考(独立使用)
python
undefinedpython
undefinedSQLAlchemy 2.0 with Pydantic validation
SQLAlchemy 2.0与Pydantic验证
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import DeclarativeBase
from pydantic import BaseModel, ConfigDict
class Base(DeclarativeBase):
pass
class UserDB(Base):
tablename = 'users'
id = Column(Integer, primary_key=True)
username = Column(String(50))
email = Column(String(100))
class UserSchema(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
username: str
email: str
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import DeclarativeBase
from pydantic import BaseModel, ConfigDict
class Base(DeclarativeBase):
pass
class UserDB(Base):
tablename = 'users'
id = Column(Integer, primary_key=True)
username = Column(String(50))
email = Column(String(100))
class UserSchema(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
username: str
email: str
Convert ORM to Pydantic
ORM转Pydantic
user_orm = db.query(UserDB).first()
user_validated = UserSchema.model_validate(user_orm)
undefineduser_orm = db.query(UserDB).first()
user_validated = UserSchema.model_validate(user_orm)
undefinedQuick Pytest Testing Reference (Inlined for Standalone Use)
快速Pytest测试参考(独立使用)
python
undefinedpython
undefinedTesting Pydantic models with pytest
使用pytest测试Pydantic模型
import pytest
from pydantic import ValidationError
def test_user_validation():
user = User(id=1, name='Alice', email='alice@example.com')
assert user.name == 'Alice'
def test_validation_error():
with pytest.raises(ValidationError) as exc_info:
User(id='invalid', name='Bob', email='bob@example.com')
errors = exc_info.value.errors()
assert errors[0]['type'] == 'int_parsing'
@pytest.fixture
def sample_user():
return User(id=1, name='Alice', email='alice@example.com')
[Full integration patterns available in respective skills if deployed together]import pytest
from pydantic import ValidationError
def test_user_validation():
user = User(id=1, name='Alice', email='alice@example.com')
assert user.name == 'Alice'
def test_validation_error():
with pytest.raises(ValidationError) as exc_info:
User(id='invalid', name='Bob', email='bob@example.com')
errors = exc_info.value.errors()
assert errors[0]['type'] == 'int_parsing'
@pytest.fixture
def sample_user():
return User(id=1, name='Alice', email='alice@example.com')
[完整集成模式需与对应技能一同部署]