
在 Pydantic V2 中,处理联合类型(Union Type)时,如果不同的模型具有相似的结构,Pydantic 可能会因为缺少足够的信息而无法正确地解析 JSON 数据。例如,当两个模型都包含一个列表类型的 items 字段,且没有其他明确的区分标志时,Pydantic 可能会错误地将数据解析为其中一个模型。为了解决这个问题,可以使用区分器(Discriminator)来明确指定用于区分不同模型的字段。
区分器(Discriminator)的作用
区分器是一个字段,它的值可以用来确定应该使用联合类型中的哪个模型来解析数据。通过在 Pydantic 模型中指定区分器,可以确保数据被正确地解析为预期的类型。
如何使用区分器
以下是一个示例,展示了如何在 Pydantic V2 中使用区分器:
import datetime
from typing import Annotated, Union, List, Literal
import pydantic
from pydantic import Field
class MealsService(pydantic.BaseModel):
class MealItem(pydantic.BaseModel):
course: str
name: str
quantity: int
unitPrice: float | None
type: Literal['meals'] = "meals"
items: list[MealItem]
time: datetime.time | None
class CanapesService(pydantic.BaseModel):
class CanapeItem(pydantic.BaseModel):
name: str
quantity: int
unitPrice: float | None
type: Literal['canapes'] = "canapes"
items: list[CanapeItem]
time: datetime.time | None
Services = Annotated[Union[MealsService, CanapesService], Field(discriminator='type')]
class Event(pydantic.BaseModel):
services: List[Services]
# 示例 JSON 数据
data = {
"services": [
{
"type": "canapes",
"items": [],
"time": None
}
]
}
# 解析 JSON 数据
event = Event(**data)
# 打印解析结果
print(event)代码解释:
- 定义模型: 首先定义了 MealsService 和 CanapesService 两个 Pydantic 模型,它们都包含 type 字段,用于区分不同的服务类型。注意type字段使用了Literal['meals']和Literal['canapes'],确保该字段的值只能是预定义的值。
- 使用 Annotated 和 Field: 使用 Annotated 将 Union[MealsService, CanapesService] 包装起来,并通过 Field(discriminator='type') 指定 type 字段作为区分器。这意味着 Pydantic 将根据 type 字段的值来决定使用哪个模型来解析数据。
- 创建 Event 模型: 创建 Event 模型,其中 services 字段是一个包含 Services 类型的列表。
- 解析 JSON 数据: 使用 Event(**data) 将 JSON 数据解析为 Event 模型的实例。Pydantic 会根据 type 字段的值正确地将数据解析为 CanapesService 类型的实例。
注意事项:
- 确保区分器字段的值在不同的模型中是唯一的。
- 区分器字段必须存在于所有联合类型中的模型中。
- 区分器字段的类型必须是可区分的,例如字符串或枚举。
总结:
通过使用区分器,可以解决 Pydantic 在解析联合类型时可能出现的歧义问题,确保数据被正确地解析为预期的模型。这种方法在处理具有相似结构的多个模型时非常有用,可以提高数据解析的准确性和可靠性。 在定义 Pydantic 模型时,合理利用 Literal 类型,可以进一步约束字段的取值范围,增强模型的健壮性。










