Skip to content

第 3 章 字段类型与可空值 ​

前两章的文章模型只有标题、内容和阅读量。再往里加,发布状态可以用布尔值,标签是一组字符串,补充信息可以是一份字典。那暂时没有简介时怎么表示?这一章把这些字段加进模型,看看 Pydantic 怎样读取和校验它们。

这里先讲字段类型。长度限制、数值范围、默认值和自定义校验留到后续章节。

本章学习目标 ​

先把常见字段写准确:文字、整数和布尔值分别用 str、int、bool;字符串列表用 list[str],简单字典用 dict[str, str]。你还会用 str | None 表示可空值,并从报错里看出输入与字段类型哪里没对上。

字段类型与模型结构 ​

文章模型会逐步扩展成这样:

text
title        -> str
content      -> str
views        -> int
is_published -> bool
tags         -> list[str]
extra_info   -> dict[str, str]
summary      -> str | None

ArticleCreate 字段类型全景拓扑

图里列的是文章模型的字段。接下来从基础类型开始,逐步把列表、字典和可空字段加进来。

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

基础字段类型 ​

模型字段写成 字段名: 类型。例如 views: int 表示阅读量是整数,is_published: bool 表示发布状态是布尔值。

类型注解会参与输入校验。"views": "100" 可以转换成整数 100;如果输入 "很多",Pydantic 无法转换,就会报错。布尔值则适合表示是否发布、是否启用这类状态。

先给文章模型增加发布状态:

py
from pydantic import BaseModel


class ArticleCreate(BaseModel):
    title: str
    content: str
    views: int
    is_published: bool


data = {
    "title": "Pydantic 字段类型入门",
    "content": "这是一篇介绍字段类型的文章",
    "views": "100",
    "is_published": True,
}

article = ArticleCreate.model_validate(data)

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

运行:

bash
uv run python main.py

可能看到类似输出:

text
100
<class 'int'>
True
<class 'bool'>

从输出可以看到,"100" 已经变成整数,is_published 则是布尔值。校验通过后,字段仍按对应的 Python 类型使用。

列表与字典字段 ​

文章通常有多个标签,所以 tags 可以写成 list[str];补充信息是一组键值对,可以写成 dict[str, str]。前者要求列表元素是字符串,后者要求字典的键和值都是字符串。

把这两个字段加入模型:

py
from pydantic import BaseModel


class ArticleCreate(BaseModel):
    title: str
    content: str
    views: int
    is_published: bool
    tags: list[str]
    extra_info: dict[str, str]


data = {
    "title": "Pydantic 字段类型入门",
    "content": "这是一篇介绍字段类型的文章",
    "views": 100,
    "is_published": True,
    "tags": ["Python", "Pydantic", "数据校验"],
    "extra_info": {
        "source": "课程案例",
        "level": "入门",
    },
}

article = ArticleCreate.model_validate(data)

print(article.tags)
print(article.tags[0])
print(article.extra_info)
print(article.extra_info["source"])

运行:

bash
uv run python main.py

可能看到类似输出:

text
['Python', 'Pydantic', '数据校验']
Python
{'source': '课程案例', 'level': '入门'}
课程案例

article.tags 还是列表,可以取出第一个标签;article.extra_info 还是字典,可以通过键取到来源。这里先用简单字典,嵌套模型留到后面再讲。

可空字段与类型错误 ​

文章简介有时还没写,可以用 summary: str | None 表示它的值可以是字符串或 None。不过,这里没有设置默认值,所以 summary 仍然是必需字段:提交数据时要提供它,只是值可以是 None。字段默认值会在下一章介绍。

下面的脚本先校验 summary 分别为 None 和字符串的两份数据,再试一次错误输入:模型要求 tags 是字符串列表,输入却给了一个字符串。

py
from pydantic import BaseModel, ValidationError


class ArticleCreate(BaseModel):
    title: str
    content: str
    views: int
    is_published: bool
    tags: list[str]
    extra_info: dict[str, str]
    summary: str | None


data = {
    "title": "Pydantic 字段类型入门",
    "content": "这是一篇介绍字段类型的文章",
    "views": 100,
    "is_published": True,
    "tags": ["Python", "Pydantic"],
    "extra_info": {
        "source": "课程案例",
        "level": "入门",
    },
    "summary": None,
}

article = ArticleCreate.model_validate(data)

print(article.summary)
print(type(article.summary))

text_data = {**data, "summary": "这是一篇字段类型介绍"}
text_article = ArticleCreate.model_validate(text_data)
print(text_article.summary)
print(type(text_article.summary))


bad_data = {
    "title": "Pydantic 字段类型入门",
    "content": "这是一篇介绍字段类型的文章",
    "views": 100,
    "is_published": True,
    "tags": "Python",
    "extra_info": {
        "source": "课程案例",
        "level": "入门",
    },
    "summary": None,
}

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

运行:

bash
uv run python main.py

错误输出可能还包含输入值和错误类型;先看字段名和核心原因:

text
None
<class 'NoneType'>
这是一篇字段类型介绍
<class 'str'>
数据校验失败
1 validation error for ArticleCreate
tags
  Input should be a valid list

前四行显示 summary 可以接收 None 或字符串。错误指向 tags:模型要求列表,输入却是字符串。

这几种类型可以放在一起查:

类型写法适合表示
字符串title: str标题、内容、分类
整数views: int阅读量、点赞数
布尔值is_published: bool是否发布、是否置顶
字符串列表tags: list[str]标签、关键词
字符串字典extra_info: dict[str, str]简单补充信息
可空字符串summary: str | None简介、备注

类型校验规则与自动类型转换决策

综合校验 ​

现在把这些字段放进同一份文章数据里。正确输入会生成模型对象;错误输入的 tags 虽然是列表,但第二个元素是整数,不符合 list[str]。

py
from pydantic import BaseModel, ValidationError


class ArticleCreate(BaseModel):
    title: str
    content: str
    views: int
    is_published: bool
    tags: list[str]
    extra_info: dict[str, str]
    summary: str | None


data = {
    "title": "Pydantic 字段类型入门",
    "content": "这是一篇介绍字段类型的文章",
    "views": "100",
    "is_published": True,
    "tags": ["Python", "Pydantic", "数据校验"],
    "extra_info": {
        "source": "课程案例",
        "level": "入门",
    },
    "summary": None,
}

article = ArticleCreate.model_validate(data)

print("文章标题:", article.title)
print("阅读量:", article.views)
print("是否发布:", article.is_published)
print("文章标签:", article.tags)
print("来源:", article.extra_info["source"])
print("文章简介:", article.summary)


bad_data = {
    "title": "Pydantic 字段类型入门",
    "content": "这是一篇介绍字段类型的文章",
    "views": 100,
    "is_published": True,
    "tags": ["Python", 123, "数据校验"],
    "extra_info": {
        "source": "课程案例",
        "level": "入门",
    },
    "summary": None,
}

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

运行:

bash
uv run python main.py

可能看到类似输出:

text
文章标题: Pydantic 字段类型入门
阅读量: 100
是否发布: True
文章标签: ['Python', 'Pydantic', '数据校验']
来源: 课程案例
文章简介: None
错误数据校验失败
1 validation error for ArticleCreate
tags.1
  Input should be a valid string

前半段是校验成功后的字段值。错误里的 tags.1 指向列表索引 1,也就是第二个元素;这里传入的是整数,不是字符串。

字段类型校验与转换全流程

前一张图展示模型有哪些字段,这张图展示输入怎样经过校验。字段类型既描述数据结构,也决定 Pydantic 如何转换和检查输入。

本章小结 ​

字段类型写清楚,Pydantic 才知道该怎样处理输入。str | None 允许值为 None,却不代表这个字段可以不传;没有默认值时,它仍是必需字段。输入类型不匹配、又无法转换,就会收到 ValidationError。