在 Python 中使用 Pydantic 的 BaseModel 进行数据验证
flyfish
BaseModel 是什么?
拆字面意思:Base + Model 是什么?
- Base
字面:基础的、基底的
编程:基类 / 父类 ------ 表示这是一个被继承的基础模板类,不能直接拿来用,必须让别的类继承它,才能获得它的能力。 - Model
字面:模型
编程:数据模型 ------ 表示这是用来定义数据长什么样的抽象结构(比如:用户有姓名、年龄、邮箱;订单有编号、金额、时间)。
python
# 必须继承它,创建自己的 具体模型
class User(BaseModel): # User 是具体模型,BaseModel 是它的基础
name: str
Base = 基础、父类、模板,只负责被继承,不负责直接使用。
| 名字部分 | 含义 | 作用 |
|---|---|---|
| Base | 基础父类 | 类继承它,获得校验/转换能力 |
| Model | 数据模型 | 定义数据的结构和格式 |
类型注解 = 给变量/参数贴一个 规定类型 的标签 是一种语法标记
Python 里的类型注解,长这样:
python
# 变量名: 类型
name: str = "小明"
age: int = 18
is_student: bool = True
str = 字符串(文字)
int = 整数(数字)
bool = 布尔值(是/否)
冒号后面的 str/int/bool,就是类型注解。
最简示例 继承 BaseModel,通过类型注解声明字段
cpp
from pydantic import BaseModel
# 2. 定义数据模型,继承 BaseModel
class User(BaseModel):
# 字段名: 类型注解
name: str # 字符串类型
age: int # 整型
email: str # 字符串类型
# 3. 创建模型实例(传入合法数据)
user = User(
name="小明",
age=22,
email="xiaoming@example.com"
)
# 4. 访问模型属性(和普通类一样)
print("用户名:", user.name)
print("年龄:", user.age)
# 5. 模型转字典(Pydantic V2 推荐方法)
print("\n模型转字典:", user.model_dump())
# 6. 模型转 JSON 字符串
print("模型转 JSON:", user.model_dump_json())
输出
cpp
用户名: 小明
年龄: 22
模型转字典: {'name': '小明', 'age': 22, 'email': 'xiaoming@example.com'}
模型转 JSON: {"name":"小明","age":22,"email":"xiaoming@example.com"}
静态语言自带编译期类型检查,传错类型直接不让运行
Python 是动态的,没有编译检查,类型错了只能运行时崩溃
用 Python 写代码特别省事、速度特别快,但它有个很头疼的问题,不管是接口传进来的参数、配置文件里的内容,还是咱们业务用到的数据,都得自己手动写代码判断:这个值是不是数字?是不是文字?格式规不规范?
Pydantic 这个工具,就是专门解决这个麻烦的!BaseModel 是它重要的功能,它靠着 Python 自带的简单标注,全自动检查数据、把数据转成正确的格式、还能轻松转换成接口需要的 JSON / 字典格式。
自动数据校验
当传入不合法数据 时,BaseModel 会直接抛出清晰的验证错误
python
from pydantic import BaseModel, ValidationError
class User(BaseModel):
name: str
age: int
email: str
# 传入错误数据:age 是字符串,不符合 int 类型
try:
invalid_user = User(
name="小红",
age="二十二", # 非法数据
email="xiaohong@example.com"
)
# 捕获验证错误
except ValidationError as e:
print("数据验证失败:")
print(e)
运行结果:
数据验证失败:
1 validation error for User
age
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='二十二', input_type=str]
For further information visit https://errors.pydantic.dev/2.12/v/int_parsing
自动类型转换
BaseModel 会智能转换兼容类型(如字符串数字 → 整型、数字 → 布尔值)
python
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
is_vip: bool
# 传入字符串数字、数字布尔值
user = User(
name="小李",
age="25", # 字符串"25" → 自动转为 int 25
is_vip=1 # 数字1 → 自动转为 bool True
)
print(user.age, type(user.age)) # 25 <class 'int'>
print(user.is_vip, type(user.is_vip)) # True <class 'bool'>
可选字段 + 默认值
实际开发中,很多字段是非必填的,可以用 Optional 定义可选字段,或直接设置默认值:
python
from pydantic import BaseModel
from typing import Optional # 导入可选类型
class User(BaseModel):
name: str
age: int
# 可选字段:默认值为 None
address: Optional[str] = None
# 普通默认值字段
is_active: bool = True
# 不传入 address、is_active,使用默认值
user = User(name="小王", age=23)
print(user.model_dump())
# 输出:{'name': '小王', 'age': 23, 'address': None, 'is_active': True}
嵌套数据模型
复杂业务数据(如用户+地址)支持嵌套模型 ,BaseModel 会递归校验所有嵌套数据
python
from pydantic import BaseModel
# 子模型:地址
class Address(BaseModel):
province: str
city: str
street: str
# 父模型:用户(包含地址)
class User(BaseModel):
name: str
age: int
address: Address # 嵌套子模型
# 创建嵌套实例
user = User(
name="小张",
age=24,
address={
"province": "山东省",
"city": "济南市",
"street": "经十路"
}
)
print("嵌套模型数据:", user.address.city) # 深圳市
print("完整字典:", user.model_dump())
运行结果:
cpp
嵌套模型数据: 济南市
完整字典: {'name': '小张', 'age': 24, 'address': {'province': '山东省', 'city': '济南市', 'street': '经十路'}}
字段约束(Field)
使用 Field 可以给字段添加长度、范围、描述等约束,让校验更精细:
python
from pydantic import BaseModel, Field, ValidationError
class User(BaseModel):
# 用户名:长度2-20
name: str = Field(min_length=2, max_length=20, description="用户名")
# 年龄:0-150
age: int = Field(gt=0, lt=150, description="合法年龄")
email: str
# 测试非法数据:名字太短,年龄超标
try:
user = User(name="张", age=200, email="test@example.com")
except ValidationError as e:
print(e.errors())
运行结果:
cpp
[{'type': 'string_too_short', 'loc': ('name',), 'msg': 'String should have at least 2 characters', 'input': '张', 'ctx': {'min_length': 2}, 'url': 'https://errors.pydantic.dev/2.12/v/string_too_short'}, {'type': 'less_than', 'loc': ('age',), 'msg': 'Input should be less than 150', 'input': 200, 'ctx': {'lt': 150}, 'url': 'https://errors.pydantic.dev/2.12/v/less_than'}]
自定义校验器(field_validator)
对于复杂校验(如邮箱格式、手机号格式),可以用 field_validator 编写自定义验证逻辑:
python
from pydantic import BaseModel, field_validator, ValidationError
import re # 正则表达式校验邮箱
class User(BaseModel):
name: str
age: int
email: str
# 自定义邮箱校验器
@field_validator('email')
def validate_email_format(cls, value):
# 正则匹配标准邮箱格式
if not re.match(r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$', value):
raise ValueError("邮箱格式不合法")
return value
# 测试非法邮箱
try:
user = User(name="小陈", age=25, email="test#example.com")
except ValidationError as e:
print("自定义校验错误:", e.errors()[0]["msg"])
# 输出:自定义校验错误:邮箱格式不合法
禁止额外字段
默认情况下,BaseModel 会忽略未定义的字段;通过配置 ConfigDict 可以禁止传入额外字段,避免脏数据:
python
from pydantic import BaseModel, ConfigDict,ValidationError
class User(BaseModel):
name: str
age: int
# 模型配置:禁止额外字段
model_config = ConfigDict(extra="forbid")
# 传入未定义的字段 phone,触发错误
try:
user = User(name="小赵", age=26, phone="13800138000")
except ValidationError as e:
print(e
运行结果:
cpp
1 validation error for User
phone
Extra inputs are not permitted [type=extra_forbidden, input_value='13800138000', input_type=str]