查看: 121|回复: 0

Python Pydantic数据校验中字段约束验证器与序列化用法详解

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
Pydantic 是目前 Python 生态中最常用的数据校验库之一。它的核心思路是把数据模型声明成带类型注解的类,然后让框架自动完成数据校验、类型转换和序列化。对于需要处理 API 请求、配置文件或数据库记录的开发者来说,Pydantic 能显著减少手写校验代码的量。

在 FastAPI 中,Pydantic 是默认的请求体和响应模型基础。除此之外,它在独立脚本、后台任务以及配置管理模块里也很实用。下面从安装开始,逐步梳理 Pydantic V2 的核心用法。
  1. pip install pydantic
复制代码

安装后可以通过一行命令确认版本:
  1. python -c "import pydantic; print(pydantic.__version__)"
复制代码

一、定义模型与基本校验

Pydantic 的数据模型通过继承 BaseModel 创建,类属性必须带类型注解。创建实例时,框架会自动校验传入的数据:
  1. from pydantic import BaseModel
  2. class User(BaseModel):
  3.     name: str
  4.     age: int
  5.     email: str
  6. user = User(name="张三", age=25, email="zhangsan@example.com")
  7. print(user)
复制代码

如果传入值的类型不匹配,但可以进行转换,Pydantic 会执行自动类型转换。比如 age 传入字符串 "30" 时,最终会变成整数 30:
  1. user = User(name="李四", age="30", email="lisi@example.com")
  2. print(user.age, type(user.age))
复制代码

如果数据完全无法通过校验,比如 age 传入 "abc",Pydantic 会抛出 ValidationError。实际项目中通常用 try/except 捕获这个异常并返回给调用方:
  1. from pydantic import ValidationError
  2. try:
  3.     User(name="王五", age="abc", email="wangwu@example.com")
  4. except ValidationError as e:
  5.     print(e)
复制代码

二、常用字段类型与约束

Pydantic 支持 Python 标准类型:str、int、float、bool、list、dict、tuple、set,也支持 typing 模块中的 Optional、List、Dict、Union 等。比如下面的 Order 模型:
  1. from typing import Optional, List, Dict, Union
  2. from pydantic import BaseModel
  3. class Order(BaseModel):
  4.     order_id: int
  5.     items: List[str]
  6.     metadata: Dict[str, str]
  7.     discount: Optional[float] = None
  8.     status: Union[str, int] = "pending"
复制代码

字段级约束通过 Field 函数实现。常见参数包括字符串长度、数值大小范围和正则匹配。下面是一个 Product 模型示例:
  1. from pydantic import BaseModel, Field
  2. class Product(BaseModel):
  3.     name: str = Field(..., min_length=1, max_length=50)
  4.     price: float = Field(..., gt=0, le=10000)
  5.     quantity: int = Field(0, ge=0)
  6.     description: str = Field(default="", max_length=200)
复制代码

约束参数的语义如下:min_length/max_length 控制字符串长度;gt/ge/lt/le 分别表示大于、大于等于、小于、小于等于;pattern 用于正则校验;default_factory 可以生成动态默认值。

正则表达式在用户输入校验中非常实用。比如用户名只能包含字母数字和下划线,手机号需要匹配国内手机号规则:
  1. from pydantic import BaseModel, Field
  2. class Account(BaseModel):
  3.     username: str = Field(..., pattern=r"^[a-zA-Z0-9_]{3,20}$")
  4.     phone: str = Field(..., pattern=r"^1[3-9]\d{9}$")
复制代码

三、验证器:处理复杂校验逻辑

当内置约束不够用时,可以编写自定义验证器。V2 版本使用 field_validator 装饰器处理字段级校验,注意验证器需要定义成类方法:
  1. from pydantic import BaseModel, field_validator
  2. class Registration(BaseModel):
  3.     username: str
  4.     password: str
  5.     confirm_password: str
  6.     @field_validator("username")
  7.     @classmethod
  8.     def username_not_admin(cls, v: str) -> str:
  9.         if v.lower() == "admin":
  10.             raise ValueError("用户名不能为 admin")
  11.         return v
  12.     @field_validator("confirm_password")
  13.     @classmethod
  14.     def passwords_match(cls, v: str, info) -> str:
  15.         if "password" in info.data and v != info.data["password"]:
  16.             raise ValueError("两次输入的密码不一致")
  17.         return v
复制代码

字段级验证器可以拿到当前字段值,也可以通过 info.data 访问其他已经校验过的字段值,因此用于确认密码之类的场景比较方便。

如果校验逻辑涉及到多个字段的联动,比如日期范围,可以使用 model_validator。mode="after" 表示验证器在字段校验完成后执行,可以访问模型实例的完整数据:
  1. from pydantic import BaseModel, model_validator
  2. class DateRange(BaseModel):
  3.     start_date: str
  4.     end_date: str
  5.     @model_validator(mode="after")
  6.     def check_date_range(self):
  7.         if self.start_date > self.end_date:
  8.             raise ValueError("开始日期不能晚于结束日期")
  9.         return self
复制代码

四、序列化与解析

Pydantic V2 中,model_dump() 用于把模型转换成字典,model_dump_json() 用于转换成 JSON 字符串。解析字典和 JSON 字符串则分别使用 model_validate() 和 model_validate_json()。
  1. user = User(name="张三", age=25, email="zhangsan@example.com")
  2. data = user.model_dump()
  3. print(data)
  4. # {'name': '张三', 'age': 25, 'email': 'zhangsan@example.com'}
  5. json_str = user.model_dump_json()
  6. print(json_str)
  7. # {"name":"张三","age":25,"email":"zhangsan@example.com"}
复制代码

反向操作示例:
  1. data_dict = {"name": "李四", "age": 30, "email": "lisi@example.com"}
  2. user = User.model_validate(data_dict)
  3. json_str = '{"name": "王五", "age": 28, "email": "wangwu@example.com"}'
  4. user = User.model_validate_json(json_str)
复制代码

五、嵌套模型

面对层级化数据,Pydantic 支持模型嵌套。子模型可以直接作为父模型字段的类型声明,实例化父模型时传入子模型实例或字典均可:
  1. from typing import List
  2. from pydantic import BaseModel
  3. class Address(BaseModel):
  4.     city: str
  5.     street: str
  6.     zip_code: str
  7. class Customer(BaseModel):
  8.     name: str
  9.     address: Address
  10.     orders: List[dict] = []
  11. customer = Customer(
  12.     name="赵六",
  13.     address={"city": "北京", "street": "中关村大街", "zip_code": "100080"},
  14.     orders=[{"order_id": 1, "amount": 99.9}],
  15. )
  16. print(customer.address.city)
复制代码

嵌套模型让 API 请求体的复杂结构可以保持清晰,同时自动完成深层校验。

六、配置管理:Settings 模型

Pydantic 官方把配置读取功能独立到了 pydantic-settings 包中。使用前需要先安装:
  1. pip install pydantic-settings
复制代码

这个模块可以从环境变量或 .env 文件自动加载配置,适合和项目部署环境配合:
  1. from pydantic_settings import BaseSettings
  2. class Settings(BaseSettings):
  3.     app_name: str = "My App"
  4.     debug: bool = False
  5.     database_url: str
  6.     class Config:
  7.         env_file = ".env"
  8. settings = Settings()
  9. print(settings.database_url)
复制代码

运行时,Pydantic 会按照环境变量和 .env 文件的内容填充 Settings 中的字段,无需手动解析配置文件。

七、FastAPI 实战:用户注册接口

Pydantic 最常见的落地场景是配合 FastAPI 开发 REST API。在下面的示例中,FastAPI 会自动使用 Pydantic 模型解析请求体、校验字段,并根据 response_model 约束响应格式:
  1. from fastapi import FastAPI
  2. from pydantic import BaseModel, EmailStr, Field
  3. app = FastAPI()
  4. class UserRegister(BaseModel):
  5.     username: str = Field(..., min_length=3, max_length=20)
  6.     email: EmailStr
  7.     password: str = Field(..., min_length=8)
  8. class UserResponse(BaseModel):
  9.     username: str
  10.     email: EmailStr
  11. @app.post("/register", response_model=UserResponse)
  12. async def register(user: UserRegister):
  13.     # 模拟用户创建逻辑
  14.     return UserResponse(username=user.username, email=user.email)
复制代码

这里的 EmailStr 是 Pydantic 提供的 Email 类型,需要单独安装 email-validator 才能使用。在 FastAPI 接口中声明 UserRegister 参数后,请求体会在进入路由函数之前完成校验。校验失败时 FastAPI 会返回 422 错误和字段错误详情,无需开发者手动处理 ValidationError。

八、实际使用建议

从简单模型入手,先用 Field 里的内置约束解决大部分问题;遇到跨字段或复杂业务逻辑时再补充验证器。面对嵌套较多、深度较深的配置结构,可以优先拆分成多个子模型。

需要注意的是,Pydantic V2 的 API 与 V1 不同。V1 中的 parse_obj、parse_raw、dict()、json() 等旧方法在 V2 中已被 model_validate、model_validate_json、model_dump、model_dump_json 取代。如果项目是从旧版本升级,需要同步修改这些调用。另外,V2 的核心校验逻辑使用 Rust 实现,性能比纯 Python 方案高很多,适合在数据量较大的场景下替代手写校验。

Pydantic 的另一个价值在于和类型检查工具配合。定义的模型类同时承担了类型声明和运行时校验的双重职责,开发时能获得 IDE 的类型补全,运行时又能得到严格数据保证。当项目使用 FastAPI、Django、SQLAlchemy 等技术栈时,Pydantic 都能作为数据层与业务层之间的稳定桥梁。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-9-3 16:20 , Processed in 0.019908 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部