在 pydantic 中应用基于嵌套判别器的约束的优雅方法

问题描述 投票:0回答:1

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 pydantic-v2
1个回答
0
投票

您可以在 [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"]
        ...

希望这有帮助!

© www.soinside.com 2019 - 2024. All rights reserved.