如何让Pydantic对List[Union[PrimaryApplicant, OtherApplicant]]中的isPrimary字段进行鉴别区分?
解决Pydantic Union列表模型匹配错误:用isPrimary字段正确区分主副申请人
这个问题的核心在于Pydantic处理Union类型的默认匹配逻辑:当你定义List[Union[PrimaryApplicant, OtherApplicant]]时,Pydantic会严格按照Union中模型的顺序,逐个尝试解析列表里的每一项。如果第一个模型(PrimaryApplicant)解析失败,才会轮到第二个模型(OtherApplicant)。
但当OtherApplicant缺少必填字段时,Pydantic的匹配流程会出问题:它先尝试用PrimaryApplicant去匹配那个isPrimary=False的项——显然不符合Literal[True]的要求,直接抛出isPrimary值无效的错误;而此时因为前面的匹配已经失败,Pydantic不会再尝试用OtherApplicant去验证,自然也就不会正确提示缺失的必填字段了。
最佳解决方案:使用Pydantic鉴别器(Discriminator)
鉴别器是Pydantic专门为这种有明确区分字段的Union场景设计的功能,它能告诉Pydantic直接通过指定字段的值来选择对应的模型,彻底绕开顺序匹配的坑。
修改后的代码(适配Pydantic v1)
from datetime import date from pydantic import BaseModel, validator, Field from typing import List, Literal, Optional, Union class PrimaryApplicant(BaseModel): isPrimary: Literal[True] dateOfBirth: Optional[date] class OtherApplicant(BaseModel): isPrimary: Literal[False] dateOfBirth: date relationshipStatus: Literal["family", "friend", "other", "partner"] # 定义带鉴别器的Union类型 ApplicationApplicant = Union[PrimaryApplicant, OtherApplicant] class Application(BaseModel): # 通过discriminator指定用isPrimary字段区分模型 applicants: List[ApplicationApplicant] = Field(..., discriminator="isPrimary") @validator("applicants") def validate_applicant_limits(cls, v): primary_count = sum(1 for applicant in v if applicant.isPrimary) secondary_count = len(v) - primary_count if primary_count > 1: raise ValueError("只能有一名主申请人") if secondary_count > 1: raise ValueError("最多只能有一名其他申请人") return v def main() -> None: # 测试:OtherApplicant缺失dateOfBirth字段 data_dict = { "applicants": [ {"isPrimary": True}, {"isPrimary": False, "relationshipStatus": "family"} # 缺失dateOfBirth ] } try: Application(**data_dict) except Exception as e: print(e) if __name__ == "__main__": main()
效果验证
现在当OtherApplicant缺失必填字段时,错误提示会准确指向缺失的字段,不会再出现isPrimary的误报:
pydantic.error_wrappers.ValidationError: 1 validation error for Application applicants -> 1 -> dateOfBirth field required (type=value_error.missing)
补充:Pydantic v2的写法
如果你用的是Pydantic v2,语法会稍有调整,推荐用Annotated来结合鉴别器:
from datetime import date from pydantic import BaseModel, field_validator, Field from typing import List, Literal, Optional, Union from typing_extensions import Annotated class PrimaryApplicant(BaseModel): isPrimary: Literal[True] dateOfBirth: Optional[date] class OtherApplicant(BaseModel): isPrimary: Literal[False] dateOfBirth: date relationshipStatus: Literal["family", "friend", "other", "partner"] # 用Annotated指定鉴别器 ApplicationApplicant = Annotated[ Union[PrimaryApplicant, OtherApplicant], Field(discriminator="isPrimary") ] class Application(BaseModel): applicants: List[ApplicationApplicant] @field_validator("applicants") def validate_applicant_limits(cls, v): primary_count = sum(1 for applicant in v if applicant.isPrimary) secondary_count = len(v) - primary_count if primary_count > 1: raise ValueError("只能有一名主申请人") if secondary_count > 1: raise ValueError("最多只能有一名其他申请人") return v
核心逻辑和v1一致,都是通过discriminator指定区分字段,让Pydantic直接匹配对应模型。
内容的提问来源于stack exchange,提问作者anon_dcs3spp
相关产品推荐
相关产品推荐

