第六章:嵌套模型与复杂结构
真实数据几乎都不是扁平的。Pydantic 的嵌套能力是它最实用的部分。
6.1 模型套模型
python
class Address(BaseModel):
city: str
street: str
zipcode: str = Field(pattern=r"^\d{6}$")
class User(BaseModel):
name: str
address: Address # 嵌套模型
friends: list[Address] = [] # 嵌套模型列表
data = {
"name": "小明",
"address": {"city": "北京", "street": "长安街", "zipcode": "100000"},
"friends": [{"city": "上海", "street": "南京路", "zipcode": "200000"}],
}
user = User.model_validate(data)
print(user.address.city) # 北京 —— 可以用点号一路访问嵌套模型的校验也是递归的,错误信息会带上完整路径:friends.0.zipcode,告诉你"friends 列表第 0 个元素的 zipcode 字段错了"。
6.2 自引用模型
树形结构(评论、分类、组织架构)很常见:
python
from __future__ import annotations
class Comment(BaseModel):
content: str
replies: list[Comment] = [] # 引用自己
Comment.model_rebuild() # 解析前向引用(某些场景需要手动调用)6.3 联合类型:一个字段,多种可能
python
class Cat(BaseModel):
pet_type: str = "cat"
meow_volume: int
class Dog(BaseModel):
pet_type: str = "dog"
bark_volume: int
class Owner(BaseModel):
pet: Cat | Dog # 联合类型⚠️ 默认的"智能模式"下,Pydantic 会尝试每个类型取最匹配的一个,有时结果出乎意料。更稳妥的做法是判别联合(Discriminated Union)——用一个字段明确区分类型:
python
from typing import Literal
from pydantic import Field
class Cat(BaseModel):
pet_type: Literal["cat"] = "cat" # 判别字段
meow_volume: int
class Dog(BaseModel):
pet_type: Literal["dog"] = "dog"
bark_volume: int
class Owner(BaseModel):
pet: Cat | Dog = Field(discriminator="pet_type") # 更快、更准、报错更清晰