Skip to content

第 4 章 Field 字段约束与默认值 ​

上一章用类型注解说明了字段是什么。可到了真实业务里,title: str 还不够:标题可能太短,阅读量不能是负数,有些字段不传时还要给个合适的值。

比如这份文章模型,除了类型,还需要这些规则:

  • title 长度在 2 到 50 个字符之间,content 至少 10 个字符。
  • views 不能小于 0,也不能超过 1,000,000。
  • is_published 默认是 False,tags 默认是空列表。
  • summary 可以是 None,不传时也默认用 None,并带上字段说明。

这章先用 Field 处理这些常见需求,不展开参数大全和自定义校验器。

本章学习目标 ​

字段有了类型,还常常需要更具体的规则。这章会用 Field(default=...) 给字段设默认值,用 min_length、max_length 限制字符串或列表长度,用 ge、le 限制数字范围。写完规则后,也要能读懂相应的报错,知道 description 起什么作用。

Field 规则与默认值 ​

我们继续用文章数据。下面这张图把上一章的字段类型和本章要加的规则放在一起:

text
title        -> 字符串,长度 2 到 50
content      -> 字符串,最少 10 个字符
views        -> 整数,默认 0,范围 0 到 1,000,000
is_published -> 布尔值,默认 False
tags         -> 字符串列表,默认空列表,最多 5 个
summary      -> 可空字符串,默认 None,最多 100 个字符

从类型注解到 Field 业务约束的进阶演化

类型注解告诉 Pydantic 字段是什么类型;Field 可以再补上默认值、范围、长度或说明。

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

用 Field 设置默认值 ​

Field 是 Pydantic 提供的字段辅助函数。像这样给标题加上长度限制:

python
title: str = Field(min_length=2, max_length=50)

title 仍然是字符串,但现在至少要有 2 个字符,最多 50 个。如果只写 title: str,就没有这两条长度规则。

再看阅读量:

python
views: int = Field(default=0, ge=0, description="阅读量")

views 仍要求整数;字段没传时用 0,传入的值不能小于 0,description 则说明这个字段表示什么。

默认值处理流程

模型收到输入后,会先看字段是否提供。没提供时,有默认值就使用它,没有默认值就记录缺失错误;提供了值,则继续做类型转换和 Field 规则校验:

字段默认值与约束校验完整流转决策图

这张图是帮助理解的简化流程,不是底层源码顺序。Pydantic 默认不会校验字段的默认值,所以默认值本身也要符合字段类型和规则。需要校验默认值时,可以启用 validate_default;这里先不展开配置方式。

先从两个简单默认值开始:不传 views 时用 0,不传 is_published 时用 False。

py
from pydantic import BaseModel, Field


class ArticleCreate(BaseModel):
    title: str
    content: str
    views: int = Field(default=0)
    is_published: bool = Field(default=False)


data = {
    "title": "Pydantic Field 入门",
    "content": "这是一篇介绍 Field 的文章",
}

article = ArticleCreate.model_validate(data)

print(article.title)
print(article.content)
print(article.views)
print(article.is_published)

运行:

bash
uv run python main.py

可能看到类似输出:

text
Pydantic Field 入门
这是一篇介绍 Field 的文章
0
False

对应的字段声明是:

python
views: int = Field(default=0)
is_published: bool = Field(default=False)

没传这些字段时,模型对象里会使用默认值。title 和 content 没设默认值,仍然要在输入中提供。

长度与数值约束 ​

常用规则速查 ​

规则示例含义
默认值Field(default=0)字段缺失时使用默认值
字符串最小长度Field(min_length=2)字符串不能太短
字符串最大长度Field(max_length=50)字符串不能太长
数字大于等于Field(ge=0)数字必须大于等于 0
数字小于等于Field(le=1_000_000)数字必须小于等于 1,000,000
字段说明Field(description="文章标题")给字段补充说明
列表默认值Field(default_factory=list)字段缺失时生成新的空列表

接下来给文章标题、正文和阅读量加限制:标题长度在 2 到 50 之间,正文至少 10 个字符,阅读量不能超出 0 到 1,000,000。

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)


data = {
    "title": "Pydantic Field 入门",
    "content": "这是一篇介绍 Field 字段规则的文章",
    "views": 100,
}

article = ArticleCreate.model_validate(data)

print(article.title)
print(article.content)
print(article.views)
print(article.is_published)

too_many_views = {**data, "views": 1_000_001}
try:
    ArticleCreate.model_validate(too_many_views)
except ValidationError as e:
    print("阅读量超出上限:")
    print(e)

运行:

bash
uv run python main.py

先看到通过校验的数据,再看到阅读量 1_000_001 超出上限的错误。完整报错还会带有其他信息,这里先看字段和核心原因:

text
Pydantic Field 入门
这是一篇介绍 Field 字段规则的文章
100
False
阅读量超出上限:
1 validation error for ArticleCreate
views
  Input should be less than or equal to 1000000

限制写在字段声明里:

python
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)

min_length 和 max_length 管长度,ge 和 le 管数字范围。ge 是 greater than or equal,表示“大于等于”;le 是 less than or equal,表示“小于等于”。所以 ge=0 不允许负数,le=1_000_000 则给阅读量设了上限。

列表默认值、可空字段与字段说明 ​

数字、字符串、布尔值和 None 这样的普通默认值可以直接写成 Field(default=...)。列表是可变对象,使用 default_factory=list 可以在每个模型对象创建时得到一份新的列表:

python
views: int = Field(default=0)
tags: list[str] = Field(default_factory=list)

description 用来说明字段含义,不会增加校验规则:

python
title: str = Field(description="文章标题")

真正负责校验的仍是 min_length、max_length、ge、le 等参数。

可变对象默认值陷阱:default vs default_factory

现在让 tags 缺失时默认为空列表、最多放 5 个标签;summary 缺失时使用 None,并补上字段说明。

py
from pydantic import BaseModel, Field, ValidationError


class ArticleCreate(BaseModel):
    title: str = Field(min_length=2, max_length=50, description="文章标题")
    content: str = Field(min_length=10, description="文章内容")
    views: int = Field(default=0, ge=0, le=1_000_000, description="阅读量")
    is_published: bool = Field(default=False, description="是否发布")
    tags: list[str] = Field(default_factory=list, max_length=5, description="文章标签")
    summary: str | None = Field(default=None, max_length=100, description="文章简介")


bad_data = {
    "title": "A",
    "content": "太短",
    "views": -1,
    "tags": ["Python", "Pydantic", "数据校验", "字段规则", "入门", "超出数量"],
}

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

运行:

bash
uv run python main.py

这份输入里标题太短、正文太短、阅读量为负数,标签数量也超了。Pydantic 会把这些错误一起列出来;下面只展示错误字段和主要原因:

text
数据校验失败
4 validation errors for ArticleCreate
title
  String should have at least 2 characters
content
  String should have at least 10 characters
views
  Input should be greater than or equal to 0
tags
  List should have at most 5 items after validation, not 6

tags 的规则是:

python
tags: list[str] = Field(default_factory=list, max_length=5, description="文章标签")

字段缺失时,Pydantic 会生成一个新的空列表;标签数量不能超过 5。summary 的声明则同时表达“值可以是字符串或 None”以及“缺失时默认用 None”:

python
summary: str | None = Field(default=None, max_length=100, description="文章简介")

可空类型和默认值有关联,但含义不同:前者说明允许哪些值,后者说明输入缺失时怎么办。

默认值的填充结果 ​

刚才看的是错误数据,这次用正确数据观察默认值实际落到模型对象里的结果。仍然只传 title 和 content:

py
from pydantic import BaseModel, Field


class ArticleCreate(BaseModel):
    title: str = Field(min_length=2, max_length=50, description="文章标题")
    content: str = Field(min_length=10, description="文章内容")
    views: int = Field(default=0, ge=0, le=1_000_000, description="阅读量")
    is_published: bool = Field(default=False, description="是否发布")
    tags: list[str] = Field(default_factory=list, max_length=5, description="文章标签")
    summary: str | None = Field(default=None, max_length=100, description="文章简介")


data = {
    "title": "Pydantic Field 入门",
    "content": "这是一篇介绍 Field 字段规则的文章",
}

article = ArticleCreate.model_validate(data)

print("文章标题:", article.title)
print("文章内容:", article.content)
print("阅读量:", article.views)
print("是否发布:", article.is_published)
print("文章标签:", article.tags)
print("文章简介:", article.summary)
print("标题字段说明:", ArticleCreate.model_fields["title"].description)

article.tags.append("Python")
another_article = ArticleCreate.model_validate(data)
print("第一篇文章新增标签:", article.tags)
print("第二篇文章标签:", another_article.tags)

运行:

bash
uv run python main.py

可能看到类似输出:

text
文章标题: Pydantic Field 入门
文章内容: 这是一篇介绍 Field 的文章
阅读量: 0
是否发布: False
文章标签: []
文章简介: None
标题字段说明: 文章标题
第一篇文章新增标签: ['Python']
第二篇文章标签: []

虽然输入里只有 title 和 content,views、is_published、tags 和 summary 也都出现在模型对象中,因为它们设置了默认值。ArticleCreate.model_fields["title"].description 能读到标题说明;它只做说明,不参与校验。

代码还给第一篇文章的 tags 加入了 "Python",第二篇文章的标签仍是空列表。这能直接看出 default_factory=list 为不同模型对象创建了独立列表。

文章字段规则校验 ​

最后把默认值和规则放到一个完整案例里,分别提交正确和错误的数据:

py
from pydantic import BaseModel, Field, ValidationError


class ArticleCreate(BaseModel):
    title: str = Field(min_length=2, max_length=50, description="文章标题")
    content: str = Field(min_length=10, description="文章内容")
    views: int = Field(default=0, ge=0, le=1_000_000, description="阅读量")
    is_published: bool = Field(default=False, description="是否发布")
    tags: list[str] = Field(default_factory=list, max_length=5, description="文章标签")
    summary: str | None = Field(default=None, max_length=100, description="文章简介")


data = {
    "title": "Pydantic Field 入门",
    "content": "这是一篇介绍 Field 字段规则的文章",
    "tags": ["Python", "Pydantic"],
    "summary": "本章学习 Field 的常用字段规则",
}

try:
    article = ArticleCreate.model_validate(data)
    print("文章标题:", article.title)
    print("阅读量:", 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", "Pydantic", "数据校验", "字段规则", "入门", "超出数量"],
    "summary": "这是一段非常长的文章简介" * 20,
}

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

运行:

bash
uv run python main.py

先看正确数据怎样得到带默认值的模型对象,再看错误数据怎样在 model_validate() 阶段被拦下。错误数据同时包含过短的标题和正文、负数阅读量、过多标签,以及超长简介。

本章小结 ​

Field 可以补默认值、长度和数值范围,也能给字段写说明。普通默认值用 default;列表要生成新默认值时,用 default_factory=list。min_length、max_length 管长度,ge、le 管数值。description 方便人读,不参与校验。