本文目录导读:

- 使用
@field_validator装饰器(Pydantic v2) - 多字段联合验证
- 自定义数据类型和验证
- 使用
@validator装饰器(Pydantic v1 兼容) - 复杂的自定义验证器
- 使用
__init__和__post_init__进行验证 - 最佳实践建议
Pydantic 提供了多种实现自定义验证的方式,我来详细介绍几种常用方法:
使用 @field_validator 装饰器(Pydantic v2)
from pydantic import BaseModel, field_validator
class User(BaseModel):
name: str
age: int
email: str
@field_validator('age')
@classmethod
def validate_age(cls, v):
if v < 0 or v > 150:
raise ValueError('年龄必须在0-150之间')
return v
@field_validator('email')
@classmethod
def validate_email(cls, v):
if '@' not in v:
raise ValueError('邮箱格式不正确')
return v
# 测试
try:
user = User(name="张三", age=200, email="test@example.com")
except Exception as e:
print(e) # 会报错,因为年龄超出范围
多字段联合验证
from pydantic import BaseModel, model_validator
class RegistrationForm(BaseModel):
username: str
password: str
confirm_password: str
@model_validator(mode='after')
def check_passwords_match(self):
if self.password != self.confirm_password:
raise ValueError('两次密码输入不一致')
return self
@model_validator(mode='after')
def validate_username(self):
if len(self.username) < 3:
raise ValueError('用户名至少需要3个字符')
return self
# 测试
try:
form = RegistrationForm(
username="ab",
password="123456",
confirm_password="123456"
)
except Exception as e:
print(e)
自定义数据类型和验证
from pydantic import BaseModel
from typing import Annotated
from pydantic.functional_validators import AfterValidator
import re
def validate_phone_number(v: str) -> str:
"""自定义手机号验证"""
# 去除空格和横线
phone = v.replace(' ', '').replace('-', '')
# 验证手机号格式
pattern = r'^1[3-9]\d{9}$'
if not re.match(pattern, phone):
raise ValueError('手机号格式不正确')
return phone
# 使用 Annotated 进行类型注解
PhoneStr = Annotated[str, AfterValidator(validate_phone_number)]
class Contact(BaseModel):
name: str
phone: PhoneStr # 使用自定义类型
# 测试
try:
contact = Contact(name="李四", phone="138-0013-8000")
print(contact) # 手机号会被自动处理
except Exception as e:
print(e)
使用 @validator 装饰器(Pydantic v1 兼容)
from pydantic import BaseModel, validator
class Product(BaseModel):
name: str
price: float
quantity: int
@validator('price')
def validate_price(cls, v):
if v <= 0:
raise ValueError('价格必须大于0')
return round(v, 2)
@validator('quantity')
def validate_quantity(cls, v):
if v < 0:
raise ValueError('数量不能为负数')
return v
# 测试
try:
product = Product(name="商品", price=19.999, quantity=10)
print(product.price) # 输出 20.0 (四舍五入)
except Exception as e:
print(e)
复杂的自定义验证器
from pydantic import BaseModel, field_validator
from typing import List, Dict
import re
class Order(BaseModel):
order_id: str
items: List[Dict[str, float]]
discount: float = 0.0
@field_validator('order_id')
@classmethod
def validate_order_id(cls, v):
# 自定义订单号格式:ORD-YYYYMMDD-XXXXX
pattern = r'^ORD-\d{8}-[A-Z0-9]{5}$'
if not re.match(pattern, v):
raise ValueError(f'订单号格式不正确: {v}')
return v
@field_validator('items')
@classmethod
def validate_items(cls, v):
if not v:
raise ValueError('订单至少包含一个商品')
for item in v:
for name, price in item.items():
if not isinstance(name, str) or not name.strip():
raise ValueError('商品名称不能为空')
if price <= 0:
raise ValueError(f'商品 {name} 的价格必须大于0')
return v
@field_validator('discount')
@classmethod
def validate_discount(cls, v):
if v < 0 or v > 1:
raise ValueError('折扣必须在0到1之间')
return v
def calculate_total(self) -> float:
total = sum(
price for item in self.items
for name, price in item.items()
)
return total * (1 - self.discount)
# 测试
try:
order = Order(
order_id="ORD-20231231-ABC12",
items=[{"商品A": 100.0}, {"商品B": 200.0}],
discount=0.1
)
print(f"订单总价: {order.calculate_total()}") # 输出: 270.0
except Exception as e:
print(e)
使用 __init__ 和 __post_init__ 进行验证
from pydantic import BaseModel
from typing import Optional
class Config(BaseModel):
host: str
port: int
timeout: Optional[int] = 30
def __post_init__(self):
"""在初始化后执行额外的验证"""
if self.port < 1 or self.port > 65535:
raise ValueError('端口号必须在1-65535之间')
if not self.host.startswith(('http://', 'https://')):
self.host = f"http://{self.host}"
# 测试
config = Config(host="localhost", port=8080)
print(config.host) # 输出: http://localhost
最佳实践建议
-
选择合适的验证器:
- 单个字段验证使用
@field_validator - 多字段依赖使用
@model_validator - 复杂业务逻辑可以创建自定义类型
- 单个字段验证使用
-
错误处理:
class UserModel(BaseModel): name: str @field_validator('name') @classmethod def validate_name(cls, v): if not v: raise ValueError('名称不能为空') return v.strip() # 捕获具体错误 try: user = UserModel(name="") except ValidationError as e: print(e.errors()) # 获取详细的错误信息 -
性能考虑:
- 验证器的顺序是重要的,先验证简单的规则
- 避免在验证器中执行耗时操作
Pydantic 的验证系统非常灵活,可以根据具体需求选择合适的方式,记住在 v2 中推荐使用 @field_validator 和 @model_validator,而不是旧版的 @validator。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播