如何排除Pydantic允许空字典字段的冗余验证错误?
问题:Pydantic联合模型校验错误冗余问题
我定义了如下Pydantic模型,通过member字段作为鉴别器区分ModelX和ModelY,同时允许model字段为空字典匹配Empty模型:
from typing import Union from pydantic import BaseModel, Extra, Field from typing_extensions import Annotated, Literal class Empty(BaseModel): class Config: extra = Extra.forbid class ModelX(BaseModel): member: Literal["x"] q: str class ModelY(BaseModel): member: Literal["y"] q: None class MainModel(BaseModel): model: Union[Annotated[Union[ModelX, ModelY], Field(discriminator="member")], Empty] MainModel.parse_obj({"model": {}}) MainModel.parse_obj({"model": {"member": "x", "q": None}})
当传入{"model": {"member": "x", "q": None}}时,因为ModelX要求q为字符串不能为None,触发了校验错误,但返回的错误信息里包含两条多余的extra fields not permitted错误:
pydantic.error_wrappers.ValidationError: 3 validation errors for MainModel model -> ModelX -> q none is not an allowed value (type=type_error.none.not_allowed) model -> member extra fields not permitted (type=value_error.extra) model -> q extra fields not permitted (type=value_error.extra)
这两条额外错误并非预期,我只需要保留q字段的类型错误。
解决方案
问题根源是Pydantic处理联合类型时,会尝试匹配所有分支模型。当前定义下,传入带字段的model时,既触发了ModelX的校验错误,又触发了Empty的额外字段错误。
方案1:调整联合类型匹配顺序(推荐)
将带鉴别器的ModelX/ModelY分支放在联合类型的前面,让Pydantic优先处理鉴别器匹配,只有当匹配失败(比如没有member字段)时才尝试匹配Empty模型:
from typing import Union from pydantic import BaseModel, Extra, Field from typing_extensions import Annotated, Literal class Empty(BaseModel): class Config: extra = Extra.forbid class ModelX(BaseModel): member: Literal["x"] q: str class ModelY(BaseModel): member: Literal["y"] q: None class MainModel(BaseModel): # 优先匹配带鉴别器的ModelX/ModelY,再匹配Empty model: Union[Annotated[Union[ModelX, ModelY], Field(discriminator="member")], Empty] # 测试验证 try: MainModel.parse_obj({"model": {"member": "x", "q": None}}) except Exception as e: print(e)
运行后错误信息会简化为:
1 validation error for MainModel model -> ModelX -> q none is not an allowed value (type=type_error.none.not_allowed)
方案2:让Empty参与鉴别器匹配
给Empty模型添加一个可选的member字段,让它加入鉴别器的匹配逻辑,这样Pydantic只会根据member值匹配对应的模型,不会触发Empty的额外字段错误:
from typing import Union from pydantic import BaseModel, Extra, Field from typing_extensions import Annotated, Literal class Empty(BaseModel): member: Literal[None] = None class Config: extra = Extra.forbid class ModelX(BaseModel): member: Literal["x"] q: str class ModelY(BaseModel): member: Literal["y"] q: None class MainModel(BaseModel): model: Annotated[Union[ModelX, ModelY, Empty], Field(discriminator="member")] # 测试空字典匹配 MainModel.parse_obj({"model": {}}) # 正常匹配Empty # 测试错误输入 try: MainModel.parse_obj({"model": {"member": "x", "q": None}}) except Exception as e: print(e)
这种方式下,空字典会因为member默认是None匹配Empty模型,而带member: "x"的输入只会匹配ModelX,不会触发Empty的校验错误。
内容的提问来源于stack exchange,提问作者xi¹⁸⁶
相关产品推荐
相关产品推荐

