Как в pydantic проверять наличие нужных полей и отсутствие лишних?

Подскажите пожалуйста, как проверять наличие лишних или отсутствие обязательных полей с помощью pydantic?

В библиотеке dataclasses это делает очень просто:

from dataclasses import dataclass, field

@dataclass
class Children:
    type_name: str
    type_id: int
    disabled: bool
    children: List[str]

# Данные с полем id >>> TypeError: Children.__init__() got an unexpected keyword argument 'id'
# Данные без поля type_name  >>> TypeError: Children.__init__() missing 1 required positional argument: 'type_name'

Если в какое-то из полей в полученных данных будут отсутствовать то вернется исключение TypeError, сообщив, что есть лишний аргумент или отсутствует обязательный.

Однако в pydantic такого поведения я не наблюдаю, если в данных не будет поля, то он просто провалидирует имеющиеся, а если будут лишние, то не сообщит об этом и вернет только те поля, что есть в Pydantic

class ApiResponseModel(BaseModel):
    data: Optional[Any] = Field(default=None)
    count_rows: Optional[int] = Field(default=0)
    request_date: Optional[datetime] = Field(default=datetime.utcnow())
    requests_count: int = Field(default_factory=int)
    api_type: Optional[str] = Field(default=None)

    def __init__(self, **data):
        super().__init__(**data)
        self.count_rows = len(self.data)


class Warehouses(ApiResponseModel):

    schedule: Dict = Field(default_factory=dict)
    warehouse: Dict = Field(default_factory=dict)

    def __init__(self, **data):
        super().__init__(**data)

# Данные с полем без поля schedule после валидации просто вернуться с пустым словарём
# Если добавить в модель еще поле, то в ответе будет дополнительно это поле.

Таким образом pydantic нативно не проверяет наличие обязательных полей, но скорее всего я делаю что-то не так, так как это можно решить у pydantic?


Ответы (1 шт):

Автор решения: insolor

Смотрим ваш код:

class Warehouses(ApiResponseModel):

    schedule: Dict = Field(default_factory=dict)
    warehouse: Dict = Field(default_factory=dict)

    def __init__(self, **data):
        super().__init__(**data)

Ниже вы пишете

Данные с полем без поля schedule после валидации просто вернуться с пустым словарём

Смотрим код, видим default_factory. Параметр default_factory - это указание, какая функция будет вызвана, чтобы сгенерировать значение по умолчанию при отсутствии поля. Т.е. буквально у вас в коде написано, что если поля нет, то будет вызвана функция dict, и поле заполнится новым пустым словарем.

Если убрать default_factory, тогда будет падать с ошибкой валидации, если поле не будет передано:

from pydantic import BaseModel
from typing import Dict


class Warehouses(BaseModel):
    schedule: Dict
    warehouse: Dict


warehouses = Warehouses()

Вывод:

  File "/home/insolor/Projects/test.py", line 10, in <module>
    warehouses = Warehouses()
  File "/home/insolor/.local/lib/python3.10/site-packages/pydantic/main.py", line 165, in __init__
    __pydantic_self__.__pydantic_validator__.validate_python(data, self_instance=__pydantic_self__)
pydantic_core._pydantic_core.ValidationError: 2 validation errors for Warehouses
schedule
  Field required [type=missing, input_value={}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.3/v/missing
warehouse
  Field required [type=missing, input_value={}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.3/v/missing

Чтобы не игнорировались лишние поля, добавляете настройку model_config = ConfigDict(extra="forbid"):

from pydantic import BaseModel, ConfigDict
from typing import Dict


class Warehouses(BaseModel):
    model_config = ConfigDict(extra="forbid")
    schedule: Dict
    warehouse: Dict


warehouses = Warehouses(schedule={}, warehouse={}, sdfwef=10)

Вывод:

Traceback (most recent call last):
  File "/home/insolor/Projects/test.py", line 11, in <module>
    warehouses = Warehouses(sdfwef=10)
  File "/home/insolor/.local/lib/python3.10/site-packages/pydantic/main.py", line 165, in __init__
    __pydantic_self__.__pydantic_validator__.validate_python(data, self_instance=__pydantic_self__)
pydantic_core._pydantic_core.ValidationError: 1 validation error for Warehouses
sdfwef
  Extra inputs are not permitted [type=extra_forbidden, input_value=10, input_type=int]
    For further information visit https://errors.pydantic.dev/2.3/v/extra_forbidden

Решение для Pydantic 2.*, взято отсюда: Pydantic validations for extra fields that not defined in schema

→ Ссылка