Skip to content

第 5 章 模型校验与错误处理 ​

上一章已经用 Field 给文章字段加了长度、范围和默认值规则,也用 model_validate() 校验过字典。现在把过程展开看看:"100" 为什么能变成整数?字段缺失时,默认值什么时候补上?几项数据都不合要求时,怎样从 ValidationError 里找到问题?

这一章不引入太多新语法,重点是把前面用过的校验流程和错误信息读明白。

本章学习目标 ​

前几章已经会写字段了,这章跟着 model_validate() 走一遍校验过程:默认值何时补上,哪些基础输入会转换类型,失败又会怎样报错。你会用 try...except ValidationError 接住错误,并从中找到字段位置、原因、错误类型和原始输入。

模型校验过程 ​

继续看文章数据:

python
data = {
    "title": "Pydantic 校验流程",
    "content": "这是一篇介绍 Pydantic 校验流程的文章",
    "views": "100"
}

views 传入的是字符串,但模型要求整数;is_published、tags 和 summary 都没有传,模型为它们设置了默认值。Pydantic 会处理缺失字段和默认值,再检查已提供的值,尝试类型转换并应用 Field 规则;校验失败时会整理出错误信息。

每个完整代码块都可以复制到本地 .code/main.py。切换示例时覆盖这个文件,然后在 .code 目录运行 uv run python main.py。

校验成功后得到模型对象 ​

model_validate() 接收输入数据。符合模型要求时,它返回模型对象;不符合时会抛出 ValidationError。

python
article = ArticleCreate.model_validate(data)

从使用角度看,整个过程可以先记成这样:

text
普通字典 data
    ↓
ArticleCreate.model_validate(data)
    ↓
按字段读取输入
    ↓
处理默认值
    ↓
根据类型注解检查和转换
    ↓
检查 Field 规则
    ↓
成功:返回模型对象
失败:抛出 ValidationError

这只是帮助理解的流程图,不是底层源码的执行细节。校验成功后,article 是模型对象,可以通过 article.title、article.views 和 article.tags 访问字段。比如输入里的 "views": "100",校验后 article.views 是整数 100。

字段校验流程 ​

再把字段缺失、类型转换和规则检查放在一张图里看:

Pydantic 核心校验双轨流水线架构

读图时记住三点:默认值只在字段缺失时使用;默认情况下它不会再经过类型和 Field 规则校验;已提供的输入在类型检查后还要继续检查字段规则。多个字段同时出错时,Pydantic 可以一次收集多条错误。

先运行一份能通过校验的数据:

py
from pydantic import BaseModel, Field


class ArticleCreate(BaseModel):
    title: str = Field(min_length=2, max_length=50)
    content: str = Field(min_length=10)
    views: int = Field(default=0, ge=0, le=1_000_000)
    is_published: bool = Field(default=False)
    tags: list[str] = Field(default_factory=list, max_length=5)
    summary: str | None = Field(default=None, max_length=100)


data = {
    "title": "Pydantic 校验流程",
    "content": "这是一篇介绍 Pydantic 校验流程的文章",
    "views": "100",
}

article = ArticleCreate.model_validate(data)

print(article)
print(type(article.views))
print(article.views)
print(article.is_published)
print(article.tags)
print(article.summary)

运行:

bash
uv run python main.py

可能看到类似输出:

text
title='Pydantic 校验流程' content='这是一篇介绍 Pydantic 校验流程的文章' views=100 is_published=False tags=[] summary=None
<class 'int'>
100
False
[]
None

这里有四个结果值得留意:"100" 变成了整数;缺失的 is_published 用了 False;tags 缺失时生成空列表;summary 缺失时得到 None。

校验执行时序与对象构建全流程

类型转换失败时看 ValidationError ​

把 views 改成 "abc",它就无法转换为整数。此时 Pydantic 会抛出 ValidationError。用 try...except 捕获后,程序可以继续处理错误:

python
try:
    article = ArticleCreate.model_validate(data)
except ValidationError as e:
    print(e)

运行下面的代码,观察错误信息:

py
from pydantic import BaseModel, Field, ValidationError


class ArticleCreate(BaseModel):
    title: str = Field(min_length=2, max_length=50)
    content: str = Field(min_length=10)
    views: int = Field(default=0, ge=0, le=1_000_000)
    is_published: bool = Field(default=False)
    tags: list[str] = Field(default_factory=list, max_length=5)
    summary: str | None = Field(default=None, max_length=100)


bad_data = {
    "title": "Pydantic 校验流程",
    "content": "这是一篇介绍 Pydantic 校验流程的文章",
    "views": "abc",
}

try:
    article = ArticleCreate.model_validate(bad_data)
    print(article)
except ValidationError as e:
    print("数据校验失败")
    print(e)

运行:

bash
uv run python main.py

完整报错还会显示输入值和错误类型;这里先看字段和主要原因:

text
数据校验失败
1 validation error for ArticleCreate
views
  Input should be a valid integer, unable to parse string as an integer

错误字段是 views,原因是 "abc" 无法转换成整数。虽然模型给 views 设了默认值 0,但默认值只在字段缺失时使用;既然输入里提供了 "abc",Pydantic 就会校验它,不会换成默认值。

多字段错误与结构化信息 ​

直接打印错误对象:

python
print(e)

适合先看有几处错误、哪些字段出错。要逐条分析,可以调用 e.errors(),它会返回结构化错误列表。每一项通常是一个字典:

字段含义
loc错误发生的位置
msg错误说明
type错误类型
input导致错误的输入值

让多个字段同时出错,再观察这两种输出:

py
from pydantic import BaseModel, Field, ValidationError


class ArticleCreate(BaseModel):
    title: str = Field(min_length=2, max_length=50)
    content: str = Field(min_length=10)
    views: int = Field(default=0, ge=0, le=1_000_000)
    is_published: bool = Field(default=False)
    tags: list[str] = Field(default_factory=list, max_length=5)
    summary: str | None = Field(default=None, max_length=100)


bad_data = {
    "title": "A",
    "content": "太短",
    "views": -1,
    "tags": "Python",
    "summary": "这是一段非常长的文章简介" * 20,
}

try:
    article = ArticleCreate.model_validate(bad_data)
    print(article)
except ValidationError as e:
    print("普通错误信息:")
    print(e)

    print("结构化错误信息:")
    for error in e.errors():
        print(error)

运行:

bash
uv run python main.py

输出比较长,下面只截取错误总数和第一条结构化信息:

text
普通错误信息:
5 validation errors for ArticleCreate
...
结构化错误信息:
{'type': 'string_too_short', 'loc': ('title',), 'msg': 'String should have at least 2 characters', 'input': 'A', 'ctx': {'min_length': 2}, 'url': 'https://errors.pydantic.dev/2.13/v/string_too_short'}
...

这份数据有五个问题:title 和 content 太短,views 是负数,tags 不是列表,summary 太长。print(e) 能快速扫一遍;e.errors() 则方便程序按 loc、msg、type 和 input 逐项处理。

e.errors() 里的四个字段 ​

字段含义示例
loc错误发生的位置('title',)
msg错误说明String should have at least 2 characters
type错误类型string_too_short
input导致错误的输入值'A'

可以把它们分别理解为定位、阅读、分类和回查输入的依据。

ValidationError 结构化错误字典深度剖析

提取错误字段 ​

有时完整的 e.errors() 太长,只看重点就够了。下面的代码按错误项提取 loc、msg、type 和 input:

py
from pydantic import BaseModel, Field, ValidationError


class ArticleCreate(BaseModel):
    title: str = Field(min_length=2, max_length=50)
    content: str = Field(min_length=10)
    views: int = Field(default=0, ge=0, le=1_000_000)
    is_published: bool = Field(default=False)
    tags: list[str] = Field(default_factory=list, max_length=5)
    summary: str | None = Field(default=None, max_length=100)


bad_data = {
    "title": "A",
    "content": "太短",
    "views": -1,
    "tags": "Python",
    "summary": "这是一段非常长的文章简介" * 20,
}

try:
    article = ArticleCreate.model_validate(bad_data)
    print(article)
except ValidationError as e:
    for error in e.errors():
        print("出错字段:", error["loc"])
        print("错误说明:", error["msg"])
        print("错误类型:", error["type"])
        print("输入值:", error["input"])
        print("-" * 30)

运行:

bash
uv run python main.py

第一条错误会显示成这样,后面的字段也会按同样方式输出:

text
出错字段: ('title',)
错误说明: String should have at least 2 characters
错误类型: string_too_short
输入值: A
------------------------------

这四行来自同一条错误字典,分别对应 loc、msg、type 和 input。

文章数据校验全流程 ​

最后用一个完整示例把成功校验和错误定位串起来:

py
from pydantic import BaseModel, Field, ValidationError


class ArticleCreate(BaseModel):
    title: str = Field(min_length=2, max_length=50)
    content: str = Field(min_length=10)
    views: int = Field(default=0, ge=0, le=1_000_000)
    is_published: bool = Field(default=False)
    tags: list[str] = Field(default_factory=list, max_length=5)
    summary: str | None = Field(default=None, max_length=100)


data = {
    "title": "Pydantic 校验流程",
    "content": "这是一篇介绍 Pydantic 校验流程的文章",
    "views": "100",
}

try:
    article = ArticleCreate.model_validate(data)
    print("模型对象:", article)
    print("阅读量类型:", type(article.views))
    print("是否发布:", article.is_published)
    print("标签列表:", article.tags)
    print("文章简介:", article.summary)
except ValidationError as e:
    print("正确数据校验失败")
    print(e)


bad_data = {
    "title": "A",
    "content": "太短",
    "views": -1,
    "tags": "Python",
    "summary": "这是一段非常长的文章简介" * 20,
}

try:
    bad_article = ArticleCreate.model_validate(bad_data)
    print(bad_article)
except ValidationError as e:
    print("错误数据校验失败:")
    print(e)

    for error in e.errors():
        print("出错字段:", error["loc"])
        print("错误说明:", error["msg"])
        print("错误类型:", error["type"])
        print("输入值:", error["input"])
        print("-" * 30)

运行:

bash
uv run python main.py

脚本先用正确数据生成模型对象,再校验错误数据,并打印每条错误的位置、原因和输入值。流程可以概括为:

text
输入普通字典
    ↓
模型校验
    ↓
成功:得到模型对象
失败:得到 ValidationError
    ↓
阅读错误信息,定位问题字段

本章小结 ​

model_validate() 会按字段类型和 Field 规则处理输入。缺失字段若有默认值,就会补上;能转换的值会转成对应类型。出错时,先用 print(e) 快速看,再用 e.errors() 里的 loc、msg、type、input 逐条定位。