Pydantic 允许列表的元素具有嵌套的可区分联合。是否有任何优雅的方法可以基于内部鉴别器在子列表上应用约束(例如 MaxLen、MinLen),而无需编写自定义验证器?
例如:在下面的
PetModel
中,将Cat
的数量限制为MinLen(1),将Dog
的数量限制为MaxLen(2)
PetsModel.model_validate(
{
"pets": [
{"pet_type": "cat", "color": "black", "black_name": "black_cat_name"},
{"pet_type": "dog", "name": "dog_name"},
]
}
)
改编自https://docs.pydantic.dev/latest/concepts/unions/#nested-discriminated-unions
from typing import Literal, Union
from typing_extensions import Annotated
from pydantic import BaseModel, Field, ValidationError
class BlackCat(BaseModel):
pet_type: Literal['cat']
color: Literal['black']
black_name: str
class WhiteCat(BaseModel):
pet_type: Literal['cat']
color: Literal['white']
white_name: str
Cat = Annotated[Union[BlackCat, WhiteCat], Field(discriminator='color')]
class Dog(BaseModel):
pet_type: Literal['dog']
name: str
Pet = Annotated[Union[Cat, Dog], Field(discriminator='pet_type')]
class PetsModel(BaseModel):
pets: list[Pet] # list of pet
我知道我们可以对宠物列表应用限制,如下
from annotated_types import MaxLen, MinLen
class PetsModel(BaseModel):
pets: Annotated[list[Pet], MinLen(1), MaxLen(5)] # list of pet
但是,我想对宠物列表元素
Cat
和 Dog
的数量应用限制。
您可以在 [pydantic] 验证之后在 pets
上创建一个
字段验证器,这样您就可以像这样检查对象数组:
from annotated_types import MaxLen, MinLen
from collections import Counter
from pydantic import field_validator
class PetsModel(BaseModel):
pets: Annotated[List[Pet], MinLen(1), MaxLen(5)]
@field_validator('pets')
@classmethod
def special_rules(cls, v: List[Pet]]) -> str:
rules = {
'Dog': {'min': 1, 'max': None},
'Cat': {'min': None, 'max': 5}
}
c = Counter(v)
for key in rules:
inbounds = rules.get('min', 0) <= c[key] <= rules.get('max', float('inf')
if not inbounds:
raise ValueError("not valid value")
现在,您可以更奇特,通过
rules
将 json_schema_extra
对象放入您的字段中,如下所示:
rules = {
'Dog': {'min': 1, 'max': None},
'Cat': {'min': None, 'max': 5}
}
class PetsModel(BaseModel):
pets: Annotated[
List[Pet], MinLen(1), MaxLen(5),
Field(json_schema_extra={"rules": rules})
]
@field_validator('pets')
@classmethod
def special_rules(cls, v: List[Pet]], info: ValidationInfo) -> str:
rules = info.json_schema_extra["rules"]
...
希望这有帮助!