Pydantic字段级校验:解锁@validator的12种应用

avatar
image image

扫描二维码关注或者微信搜一搜:编程智域 前端至全栈交流与成长

探索数千个预构建的 AI 应用,开启你的下一个伟大创意


第一章:基础校验模式

1.1 类型强制转换

1
2
3
4
5
6
7
8
9
10
11
12
13
from pydantic import BaseModel, validator


class CurrencyConverter(BaseModel):
amount: str

@validator("amount", pre=True)
def string_to_float(cls, v):
return float(v.strip("$"))


# 自动转换 "$100.5" → 100.5
print(CurrencyConverter(amount="$100.5").amount)

pre验证器特性

  • 在类型转换前执行
  • 支持原始数据清洗
  • 可处理非结构化输入

第二章:格式验证

2.1 正则表达式验证

1
2
3
4
5
6
7
8
9
10
11
import re


class IdentityForm(BaseModel):
passport: str

@validator("passport")
def validate_passport(cls, v):
if not re.match(r"^[A-PR-WY][1-9]\d\s?\d{4}[A-Z]$", v):
raise ValueError("护照号码格式错误")
return v.upper().replace(" ", "")

2.2 枚举值约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from enum import Enum


class Department(Enum):
HR = 1
IT = 2


class Employee(BaseModel):
dept: int

@validator("dept")
def check_department(cls, v):
return Department(v).name # 自动转换数字为枚举名称

第三章:动态校验

3.1 跨字段依赖验证

1
2
3
4
5
6
7
8
9
class OrderForm(BaseModel):
product_type: str
weight: float

@validator("weight")
def check_weight(cls, v, values):
if values.get("product_type") == "fragile" and v > 10:
raise ValueError("易碎品不得超过10kg")
return v

3.2 环境感知校验

1
2
3
4
5
6
7
8
9
10
11
12
import os


class EnvAwareValidator(BaseModel):
api_key: str

@validator("api_key")
def check_key_format(cls, v):
env = os.getenv("APP_ENV", "dev")
if env == "prod" and len(v) < 32:
raise ValueError("生产环境密钥强度不足")
return v

第四章:安全校验

4.1 SQL注入防御

1
2
3
4
5
6
7
8
9
class QuerySafe(BaseModel):
search_term: str

@validator("search_term")
def sanitize_input(cls, v):
forbidden = ["'", ";", "--", "/*"]
if any(c in v for c in forbidden):
raise ValueError("检测到危险字符")
return v.replace("%", "\\%")

4.2 XSS攻击过滤

1
2
3
4
5
6
7
8
9
from html import escape


class CommentForm(BaseModel):
content: str

@validator("content")
def sanitize_html(cls, v):
return escape(v).replace("\n", "<br>")

第五章:高级转换

5.1 数据归一化

1
2
3
4
5
6
7
8
9
10
11
12
class AddressNormalizer(BaseModel):
street: str

@validator("street")
def standardize_address(cls, v):
replacements = {
"St.": "Street",
"Ave": "Avenue"
}
for k, v in replacements.items():
v = v.replace(k, v)
return v.title()

5.2 加密字段处理

1
2
3
4
5
6
7
8
9
10
from cryptography.fernet import Fernet


class SecureData(BaseModel):
secret: str

@validator("secret")
def encrypt_value(cls, v):
key = Fernet.generate_key()
return Fernet(key).encrypt(v.encode())

第六章:企业级实践

6.1 分布式ID验证

1
2
3
4
5
6
7
8
9
10
11
12
13
import snowflake


class SnowflakeValidator(BaseModel):
object_id: str

@validator("object_id")
def validate_snowflake(cls, v):
try:
snowflake.deconstruct(v)
return v
except Exception:
raise ValueError("非法分布式ID格式")

6.2 金融精度控制

1
2
3
4
5
6
7
8
9
10
11
12
from decimal import Decimal, ROUND_HALF_UP


class FinancialModel(BaseModel):
amount: float

@validator("amount")
def monetary_precision(cls, v):
return Decimal(str(v)).quantize(
Decimal("0.00"),
rounding=ROUND_HALF_UP
)

课后Quiz

Q1:pre验证器的执行时机是?
A) 类型转换后
B) 类型转换前
C) 最终验证阶段

Q2:防御SQL注入的最佳方法是?

  1. 字符串替换
  2. 参数化查询
  3. 正则过滤

Q3:处理多字段依赖应使用?

  • root_validator
  • 多个字段级校验器
  • 自定义__init__方法

错误解决方案速查表

错误信息原因分析解决方案
ValidationError: value is not a valid integer类型转换前未清洗数据添加pre=True验证器
ValueError: 检测到危险字符SQL注入防御生效使用参数化查询替代直接拼接
AssertionError: 校验顺序错误依赖字段未优先验证调整字段定义顺序
TypeError: 校验器返回类型错误验证器返回值与声明类型不符检查验证器逻辑

架构原则:字段校验应遵循”早失败”原则,在数据入口处完成所有验证。建议建立企业级校验规则库,通过装饰器模式实现校验逻辑的模块化管理。

余下文章内容请点击跳转至 个人博客页面 或者 扫码关注或者微信搜一搜:编程智域 前端至全栈交流与成长,阅读完整的文章:

往期文章归档: