在 Python 中使用 Pydantic 的 BaseModel 进行数据验证

在 Python 中使用 Pydantic 的 BaseModel 进行数据验证

flyfish

BaseModel 是什么?

拆字面意思:Base + Model 是什么?

  1. Base
    字面:基础的、基底的
    编程:基类 / 父类 ------ 表示这是一个被继承的基础模板类,不能直接拿来用,必须让别的类继承它,才能获得它的能力。
  2. 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]
相关推荐
xcbrand1 小时前
政府事业机构品牌全案公司有哪些
大数据·人工智能·python
HP-Patience1 小时前
【Python爬虫】验证码识别技术
爬虫·python
XS0301061 小时前
Java基础笔记(一)
java·笔记·python
21439651 小时前
网页如何运行html
jvm·数据库·python
Irene19912 小时前
(AI总结版)Rich 配置经验总结:PyCharm 终端颜色显示操作指南
python·pycharm
小张同学8242 小时前
[特殊字符]Python 进阶实战指南(PyCharm 专属优化):从高效编码到工程化落地,告别新手低效写法
开发语言·python·pycharm
2402_854808372 小时前
Golang数组和切片有什么区别_Golang数组切片对比教程【通俗】
jvm·数据库·python
2401_865439632 小时前
如何在 Go 中精确安装指定版本的模块
jvm·数据库·python
xiaotao1312 小时前
01-编程基础与数学基石:Matplotlib & Seaborn
人工智能·python·matplotlib