第 11 章 模型配置与输入行为
前面几章分别处理了字段、校验和导出。本章再看模型整体的行为,比如:
- 输入里多了模型没声明的字段,应该忽略、报错,还是保留?
- 输入不是字典,而是普通对象时,能不能从对象属性里取值?
- 文章标题、正文带有前后空格时,能不能统一清理?
这些设置不属于单个字段,本章会用 model_config 来配置。
先看三个常用配置:
extrafrom_attributesstr_strip_whitespace
本章学习目标
一条条字段规则之外,模型本身也有处理输入的方式。这章会用 ConfigDict 设置 model_config,比较 extra="ignore"、"forbid"、"allow",试着用 from_attributes=True 读取对象属性、用 str_strip_whitespace=True 清理字符串空白。学完还要分得清模型配置、字段规则、校验器和序列化器各自管哪一段。
模型配置基础
继续用文章模型做例子。
先看一份常见输入:
data = {
"title": "Pydantic 模型配置",
"content": "这是一篇介绍 model_config 的文章内容",
"views": 100,
"unknown_field": "这个字段模型里没有声明",
}unknown_field 不在模型字段里。遇到这种情况,可以选择:
- 忽略它
- 禁止它,直接报错
- 保留它
数据也不一定总是字典,有时拿到的是普通对象:
article_object.title
article_object.content
article_object.views另外,标题和正文可能带有前后空格。字段未知处理、对象属性读取和字符串清理,这些都属于模型整体行为:
每个示例都可以复制到 .code/main.py 运行;进入下一步时再用新代码覆盖它。
额外字段的默认处理
先不写配置,看看默认结果。把下面的代码保存为 .code/main.py:
from pydantic import BaseModel, Field
class ArticleDetail(BaseModel):
title: str = Field(min_length=2, max_length=50)
content: str = Field(min_length=10)
views: int = Field(default=0, ge=0)
data = {
"title": "Pydantic 模型配置",
"content": "这是一篇介绍 model_config 的文章内容",
"views": 100,
"unknown_field": "这个字段模型里没有声明",
}
article = ArticleDetail.model_validate(data)
print(article)
print(article.model_dump())
print("默认导出含 unknown_field:", "unknown_field" in article.model_dump())运行:
uv run python main.py关键输出是:
默认导出含 unknown_field: False输入里虽然有 unknown_field,模型对象和导出字典都没有它。Pydantic 默认按 extra="ignore" 处理,后面再看怎样切换这个行为。
model_config 的作用
在 Pydantic v2 里,model_config 用来设置模型整体行为:
例如:
from pydantic import BaseModel, ConfigDict
class ArticleDetail(BaseModel):
model_config = ConfigDict(extra="forbid")这条配置会作用于整个模型:输入里只要出现未声明字段,就会报错。
ConfigDict 配置写法
配置值用 ConfigDict 声明:
from pydantic import ConfigDict再放进模型类的 model_config:
model_config = ConfigDict(extra="forbid")或者:
model_config = ConfigDict(from_attributes=True)本课程统一使用 Pydantic v2 主线,不把旧版 class Config 作为主线写法。
extra 的取值
extra 决定输入中那些“模型没有声明的字段”怎么处理:
| 配置 | 含义 |
|---|---|
extra="ignore" | 忽略多余字段 |
extra="forbid" | 禁止多余字段,出现就报错 |
extra="allow" | 允许并保留多余字段 |
模型配置位置与执行流程
model_config 和字段、校验器一样,写在模型类定义里。
默认额外字段处理流程
禁止额外字段
如果希望输入更严格,可以把 extra 设为 forbid:
将下面的完整代码保存为 .code/main.py:
from pydantic import BaseModel, ConfigDict, Field, ValidationError
class ArticleDetail(BaseModel):
model_config = ConfigDict(extra="forbid")
title: str = Field(min_length=2, max_length=50)
content: str = Field(min_length=10)
views: int = Field(default=0, ge=0)
data = {
"title": "Pydantic 模型配置",
"content": "这是一篇介绍 model_config 的文章内容",
"views": 100,
"unknown_field": "这个字段模型里没有声明",
}
try:
article = ArticleDetail.model_validate(data)
print(article)
except ValidationError as error:
print(error)
print("错误类型:", error.errors()[0]["type"])运行:
uv run python main.pyfrom pydantic import BaseModel, ConfigDict, Field, ValidationError
class ArticleDetail(BaseModel):
model_config = ConfigDict(extra="forbid")
title: str = Field(min_length=2, max_length=50)
content: str = Field(min_length=10)
views: int = Field(default=0, ge=0)
data = {
"title": "Pydantic 模型配置",
"content": "这是一篇介绍 model_config 的文章内容",
"views": 100,
"unknown_field": "这个字段模型里没有声明",
}
try:
article = ArticleDetail.model_validate(data)
print(article)
except ValidationError as error:
print(error)
print("错误类型:", error.errors()[0]["type"])关键配置是:
model_config = ConfigDict(extra="forbid")extra="forbid" 只影响未声明字段的处理方式,不会要求显式传入带默认值的字段。模型必需字段是否缺失,仍按字段本身的定义校验。
运行结果中的关键行是:
错误类型: extra_forbidden适合这些情况:
- 你希望拒绝模型未声明的字段
- 你想尽早发现拼写错误
- 你不想悄悄吞掉未知字段
extra="forbid" 处理流程
额外字段模式对比
再看另外两种选择:忽略,或保留。
extra="ignore" 忽略额外字段
将下面的完整代码保存为 .code/main.py:
from pydantic import BaseModel, ConfigDict, Field
class ArticleIgnore(BaseModel):
model_config = ConfigDict(extra="ignore")
title: str = Field(min_length=2, max_length=50)
content: str = Field(min_length=10)
class ArticleAllow(BaseModel):
model_config = ConfigDict(extra="allow")
title: str = Field(min_length=2, max_length=50)
content: str = Field(min_length=10)
data = {
"title": "Pydantic 模型配置",
"content": "这是一篇介绍 extra 模式的文章内容",
"unknown_field": "这是模型未声明的字段",
}
article_ignore = ArticleIgnore.model_validate(data)
print(article_ignore)
print(article_ignore.model_dump())
article_allow = ArticleAllow.model_validate(data)
print(article_allow)
print(article_allow.model_dump())
print(article_allow.unknown_field)
print("ignore 含 unknown_field:", "unknown_field" in article_ignore.model_dump())
print("allow 含 unknown_field:", "unknown_field" in article_allow.model_dump())运行:
uv run python main.pyfrom pydantic import BaseModel, ConfigDict, Field
class ArticleIgnore(BaseModel):
model_config = ConfigDict(extra="ignore")
title: str = Field(min_length=2, max_length=50)
content: str = Field(min_length=10)
data = {
"title": "Pydantic 模型配置",
"content": "这是一篇介绍 extra 模式的文章内容",
"unknown_field": "这是模型未声明的字段",
}
article_ignore = ArticleIgnore.model_validate(data)
print(article_ignore)
print(article_ignore.model_dump())多余字段不会报错,也不会保留。
extra="allow" 保留额外字段
from pydantic import BaseModel, ConfigDict, Field
class ArticleAllow(BaseModel):
model_config = ConfigDict(extra="allow")
title: str = Field(min_length=2, max_length=50)
content: str = Field(min_length=10)
data = {
"title": "Pydantic 模型配置",
"content": "这是一篇介绍 extra 模式的文章内容",
"unknown_field": "这是模型未声明的字段",
}
article_allow = ArticleAllow.model_validate(data)
print(article_allow)
print(article_allow.model_dump())
print(article_allow.unknown_field)多余字段会进入模型并保留下来。
两个模型共用同一份 data。看末尾的检查结果:
ignore 含 unknown_field: False
allow 含 unknown_field: True若再用 forbid 处理同一份数据,则会报 extra_forbidden。
三种 extra 模式对比
三种模式也可以这样区分:
ignore:不要它,但不报错
forbid:不要它,而且报错
allow:要它,并保留入门时通常先用 ignore 或 forbid。allow 会放宽模型边界,只有确实要保留未知字段时再选它。
对象属性读取
from_attributes=True 的作用
平时最常见的输入是字典:
ArticleDetail.model_validate(data)如果输入换成普通对象,可以设置:
model_config = ConfigDict(from_attributes=True)这样 Pydantic 就会从对象属性读取字段值。下面用同一个对象比较默认行为和开启配置后的结果:
将下面的完整代码保存为 .code/main.py:
from pydantic import BaseModel, ConfigDict, Field, ValidationError
class ArticleObject:
def __init__(self, title: str, content: str, views: int):
self.title = title
self.content = content
self.views = views
class ArticleDefault(BaseModel):
title: str = Field(min_length=2, max_length=50)
content: str = Field(min_length=10)
views: int = Field(default=0, ge=0)
class ArticleDetail(BaseModel):
model_config = ConfigDict(from_attributes=True)
title: str = Field(min_length=2, max_length=50)
content: str = Field(min_length=10)
views: int = Field(default=0, ge=0)
article_object = ArticleObject(
title="Pydantic 对象转换",
content="这是一篇介绍 from_attributes 的文章内容",
views=100,
)
try:
ArticleDefault.model_validate(article_object)
except ValidationError as error:
print("未启用 from_attributes:", error.errors()[0]["type"])
article = ArticleDetail.model_validate(article_object)
print("启用 from_attributes 后:", article.title)
print(article)
print(article.title)
print(article.model_dump())运行:
uv run python main.pyfrom pydantic import BaseModel, ConfigDict, Field, ValidationError
class ArticleObject:
def __init__(self, title: str, content: str, views: int):
self.title = title
self.content = content
self.views = views
class ArticleDefault(BaseModel):
title: str = Field(min_length=2, max_length=50)
content: str = Field(min_length=10)
views: int = Field(default=0, ge=0)
class ArticleDetail(BaseModel):
model_config = ConfigDict(from_attributes=True)
title: str = Field(min_length=2, max_length=50)
content: str = Field(min_length=10)
views: int = Field(default=0, ge=0)
article_object = ArticleObject(
title="Pydantic 对象转换",
content="这是一篇介绍 from_attributes 的文章内容",
views=100,
)
try:
ArticleDefault.model_validate(article_object)
except ValidationError as error:
print("未启用 from_attributes:", error.errors()[0]["type"])
article = ArticleDetail.model_validate(article_object)
print("启用 from_attributes 后:", article.title)
print(article)
print(article.title)
print(article.model_dump())对比结果中的关键两行是:
未启用 from_attributes: model_type
启用 from_attributes 后: Pydantic 对象转换默认模型不能把 article_object 当作字典校验;启用配置后,就会从它的属性读取 title、content 和 views。
对应的配置是:
model_config = ConfigDict(from_attributes=True)配置表示:输入是对象时,尝试从对象属性读取字段。本节只演示普通对象,不展开 ORM。
对象属性读取流程
字符串空白清理
再把 model_config 和前面几种工具放在一起对照:
| 工具 | 主要解决什么问题 |
|---|---|
| 字段类型 | 字段是什么类型 |
Field | 字段有哪些基础规则 |
field_validator | 单字段输入校验 |
model_validator | 多字段联合校验 |
field_serializer | 字段导出格式 |
model_config | 模型整体行为 |
文章表单里的标题、正文可能带有前后空格。若想统一清理模型中的字符串字段,可以设置 str_strip_whitespace=True。
将下面的完整代码保存为 .code/main.py:
from pydantic import BaseModel, ConfigDict
class ArticleDetail(BaseModel):
model_config = ConfigDict(str_strip_whitespace=True)
title: str
content: str
data = {
"title": " Pydantic 模型配置 ",
"content": " 这是一篇介绍 str_strip_whitespace 的文章内容 ",
}
article = ArticleDetail.model_validate(data)
print("输入标题:", repr(data["title"]))
print("清理后标题:", repr(article.title))
print("输入正文:", repr(data["content"]))
print("清理后正文:", repr(article.content))
print(article.title)
print(article.content)运行:
uv run python main.py运行结果中的 repr() 会把前后空格显示出来:
输入标题: ' Pydantic 模型配置 '
清理后标题: 'Pydantic 模型配置'
输入正文: ' 这是一篇介绍 str_strip_whitespace 的文章内容 '
清理后正文: '这是一篇介绍 str_strip_whitespace 的文章内容'开启后,字符串字段会自动去掉前后空白。示例只演示两个字段,不再扩展其他配置项。
文章模型配置案例
最后用一个例子把本章的配置放在一起:
将下面的完整代码保存为 .code/main.py:
from pydantic import BaseModel, ConfigDict, Field, ValidationError
class ArticleIgnore(BaseModel):
model_config = ConfigDict(extra="ignore")
title: str = Field(min_length=2, max_length=50)
content: str = Field(min_length=10)
views: int = Field(default=0, ge=0)
class ArticleForbid(BaseModel):
model_config = ConfigDict(extra="forbid")
title: str = Field(min_length=2, max_length=50)
content: str = Field(min_length=10)
views: int = Field(default=0, ge=0)
class ArticleAllow(BaseModel):
model_config = ConfigDict(extra="allow")
title: str = Field(min_length=2, max_length=50)
content: str = Field(min_length=10)
views: int = Field(default=0, ge=0)
class ArticleObject:
def __init__(self, title: str, content: str, views: int):
self.title = title
self.content = content
self.views = views
class ArticleFromObject(BaseModel):
model_config = ConfigDict(from_attributes=True)
title: str = Field(min_length=2, max_length=50)
content: str = Field(min_length=10)
views: int = Field(default=0, ge=0)
class ArticleStrip(BaseModel):
model_config = ConfigDict(str_strip_whitespace=True)
title: str
content: str
extra_data = {
"title": "Pydantic 模型配置",
"content": "这是一篇介绍 model_config 的文章内容",
"views": 100,
"unknown_field": "这个字段模型里没有声明",
}
article_ignore = ArticleIgnore.model_validate(extra_data)
print(article_ignore.model_dump())
print("ignore 含 unknown_field:", "unknown_field" in article_ignore.model_dump())
try:
article_forbid = ArticleForbid.model_validate(extra_data)
print(article_forbid)
except ValidationError as error:
print(error)
print("forbid 错误类型:", error.errors()[0]["type"])
article_allow = ArticleAllow.model_validate(extra_data)
print(article_allow.model_dump())
print(article_allow.unknown_field)
print("allow 含 unknown_field:", "unknown_field" in article_allow.model_dump())
article_object = ArticleObject(
title="Pydantic 对象转换",
content="这是一篇介绍 from_attributes 的文章内容",
views=200,
)
article_from_object = ArticleFromObject.model_validate(article_object)
print(article_from_object.model_dump())
strip_data = {
"title": " Pydantic 模型配置 ",
"content": " 这是一篇介绍字符串清理的文章内容 ",
}
article_strip = ArticleStrip.model_validate(strip_data)
print("清理前标题:", repr(strip_data["title"]))
print("清理后标题:", repr(article_strip.title))运行:
uv run python main.pyfrom pydantic import BaseModel, ConfigDict, Field, ValidationError
class ArticleIgnore(BaseModel):
model_config = ConfigDict(extra="ignore")
title: str = Field(min_length=2, max_length=50)
content: str = Field(min_length=10)
views: int = Field(default=0, ge=0)
class ArticleForbid(BaseModel):
model_config = ConfigDict(extra="forbid")
title: str = Field(min_length=2, max_length=50)
content: str = Field(min_length=10)
views: int = Field(default=0, ge=0)
class ArticleAllow(BaseModel):
model_config = ConfigDict(extra="allow")
title: str = Field(min_length=2, max_length=50)
content: str = Field(min_length=10)
views: int = Field(default=0, ge=0)
class ArticleObject:
def __init__(self, title: str, content: str, views: int):
self.title = title
self.content = content
self.views = views
class ArticleFromObject(BaseModel):
model_config = ConfigDict(from_attributes=True)
title: str = Field(min_length=2, max_length=50)
content: str = Field(min_length=10)
views: int = Field(default=0, ge=0)
class ArticleStrip(BaseModel):
model_config = ConfigDict(str_strip_whitespace=True)
title: str
content: str
extra_data = {
"title": "Pydantic 模型配置",
"content": "这是一篇介绍 model_config 的文章内容",
"views": 100,
"unknown_field": "这个字段模型里没有声明",
}
article_ignore = ArticleIgnore.model_validate(extra_data)
print(article_ignore.model_dump())
print("ignore 含 unknown_field:", "unknown_field" in article_ignore.model_dump())
try:
article_forbid = ArticleForbid.model_validate(extra_data)
print(article_forbid)
except ValidationError as error:
print(error)
print("forbid 错误类型:", error.errors()[0]["type"])
article_allow = ArticleAllow.model_validate(extra_data)
print(article_allow.model_dump())
print(article_allow.unknown_field)
print("allow 含 unknown_field:", "unknown_field" in article_allow.model_dump())
article_object = ArticleObject(
title="Pydantic 对象转换",
content="这是一篇介绍 from_attributes 的文章内容",
views=200,
)
article_from_object = ArticleFromObject.model_validate(article_object)
print(article_from_object.model_dump())
strip_data = {
"title": " Pydantic 模型配置 ",
"content": " 这是一篇介绍字符串清理的文章内容 ",
}
article_strip = ArticleStrip.model_validate(strip_data)
print("清理前标题:", repr(strip_data["title"]))
print("清理后标题:", repr(article_strip.title))这里把三个配置放进各自的模型里,依次处理额外字段、对象属性读取和字符串空白。
关键输出可以对照为:
ignore 含 unknown_field: False
forbid 错误类型: extra_forbidden
allow 含 unknown_field: True
清理前标题: ' Pydantic 模型配置 '
清理后标题: 'Pydantic 模型配置'extra 模式选择
遇到多余字段时,可以按这个顺序选择:
实际选择时:
- 输入边界严格时,优先考虑
forbid - 只关心声明字段时,使用
ignore - 只有确实需要保留未知字段时,再考虑
allow
本章小结
model_config = ConfigDict(...) 管模型整体行为。多出来的字段怎么处理,看 extra;从对象属性取值,用 from_attributes=True;清理字符串前后空白,用 str_strip_whitespace=True。具体字段的类型和约束,仍写在字段声明里。