Ответ
В Pydantic для добавления пользовательской логики проверки данных используются валидаторы. Это методы модели, помеченные декоратором @validator, которые автоматически вызываются при создании или изменении экземпляра.
Валидаторы необходимы для реализации сложных бизнес-правил, которые невозможно описать стандартными типами данных (например, проверка сложности пароля или формата номера телефона).
Пример полевого валидатора:
from pydantic import BaseModel, validator, ValidationError
class UserRegistration(BaseModel):
username: str
age: int
# Валидатор для поля 'age'
@validator('age')
def check_age_is_adult(cls, v):
# 'v' - это значение поля age
if v < 18:
raise ValueError('Пользователь должен быть старше 18 лет')
return v # Валидатор всегда должен возвращать значение
try:
# Успешная валидация
user_ok = UserRegistration(username='john_doe', age=25)
print(user_ok)
# > username='john_doe' age=25
# Ошибка валидации
user_fail = UserRegistration(username='jane_doe', age=16)
except ValidationError as e:
print(e)
# > 1 validation error for UserRegistration
# > age
# > Пользователь должен быть старше 18 лет (type=value_error)
Основные возможности валидаторов:
- Применение к нескольким полям:
@validator('field1', 'field2'). - Предварительная валидация:
@validator('field', pre=True)выполняет проверку до стандартной обработки Pydantic. Полезно для приведения типов. - Валидация всей модели: Декоратор
@root_validatorпозволяет реализовать логику, зависящую от нескольких полей одновременно (например, проверка, чтоpassword_confirmсовпадает сpassword). - Повторное использование: Валидаторы можно выносить в отдельные функции и импортировать.
Ответ 18+ 🔞
А, слушай, смотри, вот эта штука — валидаторы в Pydantic. Это ж, блядь, как раз то самое место, где можно впендюрить свою, сука, бизнес-логику, которую обычными типами не опишешь. Ну там, пароль чтоб не «12345», или номер телефона чтоб не «позвони мне».
Вот смотри, как это выглядит, реально просто, ёпта.
from pydantic import BaseModel, validator, ValidationError
class UserRegistration(BaseModel):
username: str
age: int
# Валидатор для поля 'age'
@validator('age')
def check_age_is_adult(cls, v):
# 'v' - это значение поля age
if v < 18:
raise ValueError('Пользователь должен быть старше 18 лет')
return v # Валидатор всегда должен возвращать значение
try:
# Успешная валидация
user_ok = UserRegistration(username='john_doe', age=25)
print(user_ok)
# > username='john_doe' age=25
# Ошибка валидации
user_fail = UserRegistration(username='jane_doe', age=16)
except ValidationError as e:
print(e)
# > 1 validation error for UserRegistration
# > age
# > Пользователь должен быть старше 18 лет (type=value_error)
Видишь? Написал функцию, обернул в @validator, указал поле — и всё, пиздец, теперь если какой-нибудь сопляк шестнадцатилетний попробует зарегистрироваться, ему в ебальник прилетит ValidationError с понятным текстом. Красота, блядь.
А ещё, сука, они же гибкие, эти валидаторы! Можно, например, навесить один валидатор сразу на несколько полей — @validator('field1', 'field2'). Или сделать предварительную валидацию с pre=True, это когда твоя функция срабатывает ещё до того, как Pydantic сам начнёт умничать с типами. Полезно, если тебе надо данные почистить или привести к какому-то виду.
Но самое, блядь, мощное — это @root_validator. Это когда тебе надо проверить не одно поле, а всю модель целиком, потому что логика зависит от кучи полей сразу. Ну, классика: проверить, что password и password_confirm совпадают, а то какой-нибудь растяпа один раз введёт, а второй — мимо клавиатуры, и потом сидит, блядь, не может войти, а винит тебя.
И да, их можно выносить в отдельные функции и переиспользовать, как кирпичи. Не надо каждый раз одно и то же писать, ебать мои старые костыли. Создал где-то в утилитах функцию validate_phone_number, импортировал — и юзай в любой модели. Удобно же, в рот меня чих-пых!
В общем, инструмент охуенный, главное — не перестараться и не написать там такую логику, что сам через месяц не разберёшься, что эта хуйня проверяет.