模式中未定义的额外字段的 Pydantic 验证

Pydantic validations for extra fields that not defined in schema

我正在使用 pydantic 进行模式验证,当任何额外字段添加到未定义的模式时,我想抛出一个错误。

from typing import Literal, Union

from pydantic import BaseModel, Field, ValidationError


class Cat(BaseModel):
    pet_type: Literal['cat']
    meows: int


class Dog(BaseModel):
    pet_type: Literal['dog']
    barks: float


class Lizard(BaseModel):
    pet_type: Literal['reptile', 'lizard']
    scales: bool


class Model(BaseModel):
    pet: Union[Cat, Dog, Lizard] = Field(..., discriminator='pet_type')
    n: int


print(Model(pet={'pet_type': 'dog', 'barks': 3.14, 'eats': 'biscuit'}, n=1))
""" try:
    Model(pet={'pet_type': 'dog'}, n=1)
except ValidationError as e:
    print(e) """

在上面的代码中,我添加了未定义的 eats 字段。应用了 pydantic 验证,并删除了我定义的额外值作为响应。我想抛出一个错误,说 eats is not allowed for Dog 或类似的东西。有什么办法可以实现吗?

我们是否可以直接提供输入而不是 pet 对象?
print(Model({'pet_type': 'dog', 'barks': 3.14, 'eats': 'biscuit', n=1}))。我在没有 descriminator 的情况下尝试过,但是缺少与 pet_type 相关的那些特定验证。有人可以指导我如何实现其中之一吗?

Pydantic 用于使用模式验证您的输入。在您的情况下,您想删除其中一项验证功能。

我认为您应该创建一个继承自 BaseModel

的新 class
class ModifiedBaseModel(BaseModel):
    def __init__(__pydantic_self__, **data: Any) -> None:
        registered, not_registered = __pydantic_self__.filter_data(data)
        super().__init__(**registered)
        for k, v in not_registered.items():
            __pydantic_self__.__dict__[k] = v
    
    @classmethod
    def filter_data(cls, data):
        registered_attr = {}
        not_registered_attr = {}
        annots = cls.__annotations__
        for k, v in data.items():
            if k in annots:
                registered_attr[k] = v
            else:
                not_registered_attr[k] = v
        return registered_attr, not_registered_attr

然后创建验证 classes

class Cat(ModifiedBaseModel):
    pet_type: Literal['cat']
    meows: int

现在您可以创建一个新的 Cat 而不必担心未定义的属性。像这样

my_cat = Cat(pet_type='cat', meows=3, name='blacky', age=3)

第二个问题,要直接从 dict 输入,你可以使用双星号 **

Dog(**my_dog_data_in_dict)

Dog(**{'pet_type': 'dog', 'barks': 3.14, 'eats': 'biscuit', n=1})

你可以在模型初始化时使用Configextra fieldclass到forbid额外的属性(默认情况下,额外的属性会被忽略)。

例如:

from pydantic import BaseModel, Extra

class Pet(BaseModel):
    name: str

    class Config:
        extra = Extra.forbid

data = {
    "name": "some name",
    "some_extra_field": "some value",
}

my_pet = Pet.parse_obj(data)   # <- effectively the same as Pet(**pet_data)

会提出 VaidationError:

ValidationError: 1 validation error for Pet
some_extra_field
  extra fields not permitted (type=value_error.extra)

当模型“嵌套”时也适用,例如:

class PetModel(BaseModel):
    my_pet: Pet
    n: int

pet_data = {
    "my_pet": {"name": "Some Name", "invalid_field": "some value"},
    "n": 5,
}

pet_model = PetModel.parse_obj(pet_data)
# Effectively the same as
# pet_model = PetModel(my_pet={"name": "Some Name", "invalid_field": "some value"}, n=5)

将提高:

ValidationError: 1 validation error for PetModel
my_pet -> invalid_field
  extra fields not permitted (type=value_error.extra)