第 1 章 Pydantic 与输入校验
Python 项目里,经常要处理外部输入数据。
例如:文章提交到程序后,我们拿到的可能是一份普通字典:
data = {
"title": "Pydantic 入门",
"content": "这是一篇文章",
"views": "100"
}这份数据乍看没问题,views 却是字符串。字段也可能缺失,或者把 views 写成 view。如果程序直接拿它做业务处理,问题常常要等到读取或计算时才冒出来。
这一章就来解决一件事:怎样先检查输入,再把可靠的数据交给后面的代码?
本章学习目标
普通字典用起来方便,但业务代码直接拿它当可靠数据,会遇到什么问题?这一章先把风险看清,再用 BaseModel 描述数据结构。学完后,你能用 model_validate() 校验字典、读取模型对象,也能看懂基础的 ValidationError。
普通字典的使用风险
dict 很适合保存键值对,但它不会替我们检查字段是否齐全、名字是否写对,或值的类型是否符合预期。我们先不用 Pydantic,看看问题会在哪一步出现。
按课程首页的说明创建本地 .code 练习目录,把下面代码复制到 .code/main.py:
data = {
"title": "Pydantic 入门",
"content": "这是一篇文章",
"views": "100",
}
print("文章标题:", data["title"])
print("文章内容:", data["content"])
print("阅读量:", data["views"])
print("阅读量类型:", type(data["views"]))
try:
new_views = data["views"] + 1
print("新的阅读量:", new_views)
except TypeError as e:
print("阅读量计算失败:", e)
bad_data = {
"title": "Pydantic 入门",
"view": "100",
}
try:
print("错误数据中的文章内容:", bad_data["content"])
except KeyError as e:
print("字段不存在:", e)
try:
print("错误数据中的阅读量:", bad_data["views"])
except KeyError as e:
print("字段不存在:", e)运行:
uv run python main.py可能看到类似输出:
文章标题: Pydantic 入门
文章内容: 这是一篇文章
阅读量: 100
阅读量类型: <class 'str'>
阅读量计算失败: can only concatenate str (not "int") to str
字段不存在: 'content'
字段不存在: 'views'第一份数据里的 views 是字符串,和整数相加时才报错。第二份数据少了 content,还把 views 拼成了 view;代码读取必需字段时就遇到了 KeyError。字典保存了值,却没有保证这些值符合业务要求。
数据模型与输入校验
对文章数据来说,我们希望 title、content、views 都存在,而且 views 是整数。数据模型就是把这类结构要求写清楚的一种方式:
title -> str
content -> str
views -> intPydantic 提供了 BaseModel,可以用它定义模型,再把输入交给模型校验。校验通过后,拿到的是模型对象,后续可以通过属性访问字段。
用 BaseModel 描述文章数据
还是刚才的文章数据。把 .code/main.py 替换成下面的代码:
from pydantic import BaseModel
class ArticleCreate(BaseModel):
title: str
content: str
views: int
data = {
"title": "Pydantic 入门",
"content": "这是一篇文章",
"views": "100",
}
article = ArticleCreate.model_validate(data)
print(article)
print(article.title)
print(article.content)
print(article.views)
print(type(article.views))运行:
uv run python main.py可能看到类似输出:
title='Pydantic 入门' content='这是一篇文章' views=100
Pydantic 入门
这是一篇文章
100
<class 'int'>模型类 ArticleCreate 声明了字段和类型要求:
class ArticleCreate(BaseModel):
title: str
content: str
views: int用 model_validate() 校验输入
调用 ArticleCreate.model_validate(data) 后,Pydantic 会检查输入是否符合模型要求。校验成功,才会返回模型对象:
article = ArticleCreate.model_validate(data)默认情况下,模型没有声明的额外字段会被忽略。如果把必需字段名写错,比如输入里只有 view,模型仍会发现 views 缺失,校验不会通过。
访问模型字段
print(article.title)
print(article.views)这时 article 是模型对象,原始的 data 仍是普通字典。注意,输入中的 "100" 原本是字符串;因为它可以转换为整数,校验后 article.views 的值就是 100。类型注解是校验规则的一部分,但并不意味着所有类型不匹配都会被直接拒绝。
输入校验错误
可转换的 "100" 能通过校验。如果 content 缺失,或 views 是无法转成整数的 "很多",Pydantic 会抛出 ValidationError。把 .code/main.py 换成下面的代码,分别观察这两种错误:
from pydantic import BaseModel, ValidationError
class ArticleCreate(BaseModel):
title: str
content: str
views: int
missing_data = {
"title": "Pydantic 入门",
"views": "100",
}
try:
article = ArticleCreate.model_validate(missing_data)
print(article)
except ValidationError as e:
print("校验失败:")
print(e)
wrong_type_data = {
"title": "Pydantic 入门",
"content": "这是一篇文章",
"views": "很多",
}
try:
article = ArticleCreate.model_validate(wrong_type_data)
print(article)
except ValidationError as e:
print("校验失败:")
print(e)运行 uv run python main.py。错误信息可能包含其他细节,先找到出错字段和原因就够了:
校验失败:
1 validation error for ArticleCreate
content
Field required
校验失败:
1 validation error for ArticleCreate
views
Input should be a valid integer这里的两处问题分别是:content 没有提供,views 无法转换为整数。记住这条顺序:
输入字典 -> model_validate()
├─ 校验成功:得到模型对象,再读取 article.title、article.views
└─ 校验失败:抛出 ValidationError,处理错误后再继续最后用一个脚本把成功和失败两种情况放在一起看。再次替换 .code/main.py,运行 uv run python main.py:
from pydantic import BaseModel, ValidationError
class ArticleCreate(BaseModel):
title: str
content: str
views: int
data = {
"title": "Pydantic 入门",
"content": "这是一篇文章",
"views": "100",
}
try:
article = ArticleCreate.model_validate(data)
print("文章数据校验成功")
print("文章标题:", article.title)
print("文章内容:", article.content)
print("阅读量:", article.views)
print("阅读量类型:", type(article.views))
except ValidationError as e:
print("文章数据校验失败")
print(e)
bad_data = {
"title": "Pydantic 入门",
"views": "很多",
}
try:
article = ArticleCreate.model_validate(bad_data)
print("文章数据校验成功")
print("文章标题:", article.title)
print("文章内容:", article.content)
print("阅读量:", article.views)
except ValidationError as e:
print("文章数据校验失败")
print(e)运行后会先看到成功校验的文章数据,views 已经是整数;随后错误数据会校验失败。第二份数据既缺少 content,views 也无法转成整数,因此错误信息会指出这两个字段的问题。
本章小结
普通字典只负责装数据,数据是否可靠还得自己确认。用 BaseModel 定好结构,交给 model_validate() 校验;成功后再使用模型对象,失败时查看 ValidationError。先校验,再使用,后面几章都会沿着这条线走。