@albertalexandrov

Как работает annotated-types в пределах Pydantic?

Пожалуйста, объясните, как работает, например, Ge из библиотеки annotated-types. Как-то слишком много магии, что даже просмотр исходного кода не проясняет.

Смотрите сами. Пусть есть модель Pydantic (v2):

from annotated_types import Gt
from pydantic import BaseModel


class Person(BaseModel):
    first_name: str
    last_name: str
    age: Annotated[int, Gt(30)]


Исходный код для Gt:

class BaseMetadata:
    """Base class for all metadata.

    This exists mainly so that implementers
    can do `isinstance(..., BaseMetadata)` while traversing field annotations.
    """

    __slots__ = ()


class SupportsGt(Protocol):
    def __gt__(self: T, __other: T) -> bool:
        ...


@dataclass(frozen=True, **SLOTS)
class Gt(BaseMetadata):
    """Gt(gt=x) implies that the value must be greater than x.

    It can be used with any type that supports the ``>`` operator,
    including numbers, dates and times, strings, sets, and so on.
    """

    gt: SupportsGt


Как Pydantic выполняет сравнивание при валидации? И как вообще он понимает, что нужно сравнить, а не выполнить сравнение по паттерну? Я бы еще понял, что сначала мы инициализируем, а потом вызываем метод `__call__`...

Как написать свой валидатор, скажем, который выполняет сравнение на конкретную строку типа такого:

@dataclass(frozen=True, **SLOTS)
class MyCompare:
     the_string: str


?

Если входящая строка равна указанной строке, то это ошибка валидации.
  • Вопрос задан
  • 445 просмотров
Пригласить эксперта
Ответы на вопрос 2
Vindicar
@Vindicar
RTFM!
Насколько я понял, annotated-types просто задаёт стандарт для описания правил валидации полей, и способ получения этого описания для класса. А уж как это описание будет использоваться, должна решать конкретная библиотека, которая этому стандарту следует. Т.е. annotated-types сам по себе ничего не проверяет.
Впрочем, в тестах у них есть примитивный пример реализации.
Ответ написан
Комментировать
@alekssamos
Программист любитель
А зачем? Можно же сделать ттак:
class Person(BaseModel):
    age: conint(ge=30)
    class Config:
        validate_assignment = True
        validate_all = True

А вот пример со своим валидатором (это я обрабатывал checkboxes с формы):
class FormModel(BaseModel):
    first: Optional[bool] = False
    second: Optional[bool] = False
    third: Optional[bool] = False
    something_there: str = "qwexdfg"

    class Config:
        validate_assignment = True

    @validator("first", "second", "third")
    def set_bool(cls, val):
        return False if val is None else val

    @validator("something_there")
    def set_str(cls, val):
        return "x" in val

Чтобы None тоже приравнивалось к False.
И от себя вот сейчас одну переменную добавил, принять строку, только если есть буква "x"
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы