Python使用Pydantic实现数据校验的最佳实践

 更新时间:2026年03月31日 08:27:02   作者:站大爷IP  
Pydantic是一个Python库,用于数据验证和解析,这篇文章主要介绍了Python如何使用Pydantic实现数据校验功能,感兴趣的小伙伴可以跟随小编一起学习一下

​小张盯着屏幕上那一行报错,已经发了十分钟的呆。

事情是这样的。他们团队在做一个用户注册功能,前端传过来一份JSON数据,按理说应该有用户名、邮箱、年龄三个字段。结果测试同学随手填了个年龄“二十五”,程序直接炸了——类型错误,字符串不能和整数比较。

小张翻了翻代码,发现校验逻辑散落在各处:views.py里有一堆if判断,models.py里又有几个正则表达式,utils.py里还藏着个专门清洗数据的函数。改一个地方,另外两个地方就忘了同步。

他叹了口气。这种问题,在这个项目里已经出现过无数次了。

如果你也写过Python后端,你一定懂这种感觉:数据校验这件事,做起来不难,但做好很难。你永远不知道用户会传什么乱七八糟的东西进来。今天是个字符串年龄,明天可能就是空的邮箱,后天直接少传一个字段。

而且最烦的是,校验代码写多了,业务逻辑反而看不清楚。一个函数里,前面二十行都在做类型检查和判空,真正干活的代码被挤到最后几行。

我后来才知道,这个问题早就有了解法。它的名字叫Pydantic。

一、从一个最简单的例子说起

先别急着看那些复杂的文档。Pydantic最核心的东西,其实特别好理解。

假设你现在要写一个用户注册的接口。传统写法大概是这样:

def register_user(data):
    if not data.get('username'):
        raise ValueError('用户名不能为空')
    if not isinstance(data.get('age'), int):
        raise ValueError('年龄必须是数字')
    if data.get('age') < 18:
        raise ValueError('年龄必须大于18岁')
    # ... 继续校验邮箱、手机号等等
    # 校验通过后,才真正开始处理业务逻辑

这段代码的问题不是它错了,而是它把校验和业务逻辑混在一起。看代码的人需要一边理解校验规则,一边理解业务逻辑,脑子很累。

用Pydantic改一下:

from pydantic import BaseModel, Field

class User(BaseModel):
    username: str
    age: int = Field(ge=18)
    email: str

就这些。没了。

当你需要校验数据的时候:

def register_user(data):
    user = User(**data)
    # 校验已经自动完成,这里只管业务逻辑
    save_to_database(user)

如果数据不符合要求,Pydantic会自动抛出ValidationError,并且告诉你哪里错了。比如age传了字符串"二十五",它会说"Input should be a valid integer"。如果age传了16,它会说"Input should be greater than or equal to 18"。

这就是Pydantic最核心的价值:把校验规则从业务代码里抽离出来,让代码更干净,让错误信息更清晰。

二、自动类型转换,帮你省掉无数if语句

你有没有遇到过这种场景:前端传过来的JSON里,所有数字都是字符串。你拿到数据之后,得挨个转成整数或浮点数,不然没办法做数值计算。

在Pydantic里,这事是自动的。

from pydantic import BaseModel

class Product(BaseModel):
    price: float
    quantity: int

data = {"price": "19.99", "quantity": "3"}
product = Product(**data)

print(product.price)      # 19.99,已经是float
print(product.quantity)   # 3,已经是int

只要字符串的内容能安全地转换成目标类型,Pydantic就帮你自动完成。转换不了的时候,才会报错。

这个特性在对接API的时候尤其好用。你不需要再写一行一行的int(data['age']),也不需要担心哪个字段忘记转了。

三、可选字段和默认值,处理不完整数据

真实世界的数据很少是完整的。用户可能不填手机号,API可能不返回某个字段。Pydantic处理这种情况也很简单:

from pydantic import BaseModel
from typing import Optional

class UserProfile(BaseModel):
    username: str
    age: int
    phone: Optional[str] = None
    is_active: bool = True

Optional[str] = None 表示phone字段可以不存在,如果不存在就设为None。is_active = True 表示这个字段有默认值,调用方可以不传。

这样,当你接收到不完整的数据时,Pydantic会自动补全缺失的字段,而不是直接报错。

data = {"username": "张三", "age": 25}
profile = UserProfile(**data)
print(profile.phone)      # None
print(profile.is_active)  # True

这在实际开发中非常实用。你不需要写一堆data.get('phone', None)这样的代码,模型定义本身就是文档。

四、Field约束,让校验规则一目了然

刚才我们用ge=18限制了年龄必须大于等于18。Field还提供了很多其他约束:

from pydantic import BaseModel, Field

class Product(BaseModel):
    name: str = Field(min_length=1, max_length=100)
    price: float = Field(gt=0, description="价格必须大于0")
    rating: int = Field(ge=1, le=5)
    tags: list[str] = Field(max_items=10)

这些约束写在一起,比散落在各个地方的if语句好维护多了。你想改某个字段的校验规则,只需要改模型定义那一行,不用在整个代码库里到处搜。

而且这些约束不只是运行时生效,还能自动生成API文档。如果你用的是FastAPI,这些约束会自动映射到OpenAPI文档里,前端的人看一眼就知道该怎么传参数。

五、自定义校验器,处理那些复杂逻辑

有些校验规则不是简单的大小比较能搞定的。比如手机号格式、密码强度、两个字段之间的依赖关系。

这时候可以用@field_validator

from pydantic import BaseModel, field_validator
import re

class Account(BaseModel):
    username: str
    password: str
    confirm_password: str

    @field_validator('username')
    def username_alphanumeric(cls, v):
        if not v.isalnum():
            raise ValueError('用户名只能包含字母和数字')
        if len(v) < 3:
            raise ValueError('用户名至少3个字符')
        return v.lower()  # 可以顺便做规范化

    @field_validator('confirm_password')
    def passwords_match(cls, v, info):
        if v != info.data.get('password'):
            raise ValueError('两次输入的密码不一致')
        return v

注意第二个校验器,它用到了info.data来获取其他字段的值。这让你可以校验字段之间的依赖关系。

自定义校验器里还能做数据清洗。比如用户名统一转小写,电话号码去掉横线和空格。这样后面用到这些数据的时候,已经是最干净的状态了。

六、嵌套模型,处理复杂数据结构

现实中的数据往往是嵌套的。一个订单包含多个商品,每个商品又有自己的属性。Pydantic处理这种嵌套非常自然:

from pydantic import BaseModel
from typing import List

class Address(BaseModel):
    street: str
    city: str
    zip_code: str

class OrderItem(BaseModel):
    product_id: int
    quantity: int
    price: float

class Order(BaseModel):
    order_id: str
    address: Address
    items: List[OrderItem]
    total: float

当你传入嵌套的数据结构时,Pydantic会递归地校验每一层:

order_data = {
    "order_id": "ORD-001",
    "address": {"street": "123 Main St", "city": "Beijing", "zip_code": "100000"},
    "items": [
        {"product_id": 1, "quantity": 2, "price": 19.99},
        {"product_id": 2, "quantity": 1, "price": 49.99}
    ],
    "total": 89.97
}

order = Order(**order_data)

如果某个商品少了quantity字段,或者address里少了city,Pydantic会在对应的层级报错,告诉你具体是哪个位置出了问题。排查起来非常方便。

七、处理真实API响应的技巧

在实际工作中,你经常要处理各种API返回的数据。有些API返回的字段名和你代码里用的不一样,有些API返回的日期格式很奇怪。

Pydantic提供了field_alias和自定义校验器来解决这些问题:

from pydantic import BaseModel, Field, field_validator
from datetime import datetime

class APIResponse(BaseModel):
    user_id: int = Field(alias="id")
    full_name: str = Field(alias="name")
    created_at: datetime

    @field_validator('created_at', mode='before')
    def parse_date(cls, v):
        # 处理各种奇怪的日期格式
        if isinstance(v, str):
            return v.replace('Z', '+00:00')
        return v

alias让你可以用API返回的字段名,但代码里用自己习惯的名字。mode='before'的校验器在类型转换之前运行,最适合处理那些格式不统一的数据。

这样,不管外部数据有多乱,到了你的业务代码里,都是规规矩矩的Python对象。

八、性能怎么样?

有人可能会担心:加了这么多校验,会不会变慢?

Pydantic的核心校验逻辑是用Rust写的(pydantic-core),比纯Python实现快很多。官方文档说,Pydantic V2比V1快了大约17倍。

在实际项目中,校验的开销通常远小于数据库查询或网络请求的开销。所以放心用,它不是瓶颈。

九、写在最后

小张后来把项目里的数据校验全部重构成了Pydantic模型。原来散落在各个文件里的校验代码,被几十行模型定义替代了。代码量减少了,可读性提高了,bug也少了。

有一次新来的同事接手他的代码,看完模型定义之后说:“原来这个字段有这些限制,一看就懂了。”

这就是Pydantic最大的价值——它让数据校验这件事,从“到处贴胶布”变成了“一次性定义清楚”。

数据校验不该是代码里的噪音,它应该是代码的一部分,清晰、简洁、可维护。

如果你还在手动写一堆if语句做校验,不妨试试Pydantic。你会回来感谢它的。

到此这篇关于Python使用Pydantic实现数据校验的最佳实践的文章就介绍到这了,更多相关Python Pydantic数据校验内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • python 模拟贷款卡号生成规则过程解析

    python 模拟贷款卡号生成规则过程解析

    这篇文章主要介绍了python 模拟贷款卡号生成规则过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-08-08
  • Python操作Excel的学习笔记

    Python操作Excel的学习笔记

    这篇文章主要介绍了Python操作Excel的学习笔记,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-02-02
  • pytorch中的nn.Unfold()函数和fold()函数解读

    pytorch中的nn.Unfold()函数和fold()函数解读

    这篇文章主要介绍了pytorch中的nn.Unfold()函数和fold()函数用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • python实现的config文件读写功能示例

    python实现的config文件读写功能示例

    这篇文章主要介绍了python实现的config文件读写功能,结合实例形式分析了Python文件读写相关操作技巧,需要的朋友可以参考下
    2019-09-09
  • Python文件操作基础及异常处理

    Python文件操作基础及异常处理

    这篇文章主要介绍了文件操作的基本方法,包括如何打开和关闭文件、使用with语句管理文件、读取和写入文件内容、处理文件异常、进行二进制文件操作以及文件路径的说明,,需要的朋友可以参考下
    2025-03-03
  • Python约瑟夫生者死者小游戏实例讲解

    Python约瑟夫生者死者小游戏实例讲解

    在本篇文章里小编给大家分享的是一篇关于Python约瑟夫生者死者小游戏实例讲解内容,有兴趣的朋友们可以测试学习下。
    2021-01-01
  • PyQt5实现多张图片显示并滚动

    PyQt5实现多张图片显示并滚动

    最近要做个网页图片批量下载工具,然后需要一个页面显示网页上的所有图片供用户勾选,再根据勾选的内容来下载指定图片,其中就涉及到要到同时显示多张图片,本文就来介绍一下
    2021-06-06
  • Python Django中间件,中间件函数,全局异常处理操作示例

    Python Django中间件,中间件函数,全局异常处理操作示例

    这篇文章主要介绍了Python Django中间件,中间件函数,全局异常处理操作,结合实例形式分析了Django中间件,中间件函数,全局异常处理相关操作技巧,需要的朋友可以参考下
    2019-11-11
  • Python Excel vlookup函数实现过程解析

    Python Excel vlookup函数实现过程解析

    这篇文章主要介绍了Python Excel vlookup函数实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06
  • 细说NumPy数组的四种乘法的使用

    细说NumPy数组的四种乘法的使用

    这篇文章主要介绍了细说NumPy数组的四种乘法的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12

最新评论