第 5 章 模型校验与错误处理
上一章已经用 Field 给文章字段加了长度、范围和默认值规则,也用 model_validate() 校验过字典。现在把过程展开看看:"100" 为什么能变成整数?字段缺失时,默认值什么时候补上?几项数据都不合要求时,怎样从 ValidationError 里找到问题?
这一章不引入太多新语法,重点是把前面用过的校验流程和错误信息读明白。
本章学习目标
前几章已经会写字段了,这章跟着 model_validate() 走一遍校验过程:默认值何时补上,哪些基础输入会转换类型,失败又会怎样报错。你会用 try...except ValidationError 接住错误,并从中找到字段位置、原因、错误类型和原始输入。
模型校验过程
继续看文章数据:
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。
article = ArticleCreate.model_validate(data)从使用角度看,整个过程可以先记成这样:
普通字典 data
↓
ArticleCreate.model_validate(data)
↓
按字段读取输入
↓
处理默认值
↓
根据类型注解检查和转换
↓
检查 Field 规则
↓
成功:返回模型对象
失败:抛出 ValidationError这只是帮助理解的流程图,不是底层源码的执行细节。校验成功后,article 是模型对象,可以通过 article.title、article.views 和 article.tags 访问字段。比如输入里的 "views": "100",校验后 article.views 是整数 100。
字段校验流程
再把字段缺失、类型转换和规则检查放在一张图里看:
读图时记住三点:默认值只在字段缺失时使用;默认情况下它不会再经过类型和 Field 规则校验;已提供的输入在类型检查后还要继续检查字段规则。多个字段同时出错时,Pydantic 可以一次收集多条错误。
先运行一份能通过校验的数据:
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)运行:
uv run python main.py可能看到类似输出:
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 捕获后,程序可以继续处理错误:
try:
article = ArticleCreate.model_validate(data)
except ValidationError as e:
print(e)运行下面的代码,观察错误信息:
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)运行:
uv run python main.py完整报错还会显示输入值和错误类型;这里先看字段和主要原因:
数据校验失败
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 就会校验它,不会换成默认值。
多字段错误与结构化信息
直接打印错误对象:
print(e)适合先看有几处错误、哪些字段出错。要逐条分析,可以调用 e.errors(),它会返回结构化错误列表。每一项通常是一个字典:
| 字段 | 含义 |
|---|---|
loc | 错误发生的位置 |
msg | 错误说明 |
type | 错误类型 |
input | 导致错误的输入值 |
让多个字段同时出错,再观察这两种输出:
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)运行:
uv run python main.py输出比较长,下面只截取错误总数和第一条结构化信息:
普通错误信息:
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' |
可以把它们分别理解为定位、阅读、分类和回查输入的依据。
提取错误字段
有时完整的 e.errors() 太长,只看重点就够了。下面的代码按错误项提取 loc、msg、type 和 input:
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)运行:
uv run python main.py第一条错误会显示成这样,后面的字段也会按同样方式输出:
出错字段: ('title',)
错误说明: String should have at least 2 characters
错误类型: string_too_short
输入值: A
------------------------------这四行来自同一条错误字典,分别对应 loc、msg、type 和 input。
文章数据校验全流程
最后用一个完整示例把成功校验和错误定位串起来:
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)运行:
uv run python main.py脚本先用正确数据生成模型对象,再校验错误数据,并打印每条错误的位置、原因和输入值。流程可以概括为:
输入普通字典
↓
模型校验
↓
成功:得到模型对象
失败:得到 ValidationError
↓
阅读错误信息,定位问题字段本章小结
model_validate() 会按字段类型和 Field 规则处理输入。缺失字段若有默认值,就会补上;能转换的值会转成对应类型。出错时,先用 print(e) 快速看,再用 e.errors() 里的 loc、msg、type、input 逐条定位。