Короткий ответ
Для межполевой валидации в Pydantic v2 используют декоратор @model_validator(mode='after') на уровне модели, который получает уже собранный объект со всеми полями и может проверить их согласованность или выбросить ValueError.
Как это работает подробнее
@field_validator проверяет/преобразует одно поле изолированно и не гарантированно видит остальные поля в нужном порядке; @model_validator(mode='after') выполняется после того, как все отдельные поля уже провалидированы и собраны в модель, поэтому внутри него безопасно сравнивать несколько полей сразу.
Пример кода
example.py
from pydantic import BaseModel, model_validator
class DiscountIn(BaseModel):
discount_type: str # "percent" или "fixed"
value: float
@model_validator(mode="after")
def check_value_range(self):
if self.discount_type == "percent" and not (0 < self.value <= 100):
raise ValueError("percent discount must be between 0 and 100")
if self.discount_type == "fixed" and self.value <= 0:
raise ValueError("fixed discount must be positive")
return self
# DiscountIn(discount_type="percent", value=150) -> ValidationErrorЧто использовать на практике
- Практика. Для вложенных моделей (поле типа другой
BaseModelилиlist[OtherModel]) Pydantic валидирует вложенные объекты рекурсивно; межполевые проверки на уровне вложенного объекта делают в его собственномmodel_validator, а проверки, затрагивающие и внешние, и вложенные поля, — вmodel_validatorвнешней модели.
Подводные камни
- Риск. Пытаются провести межполевую проверку в
field_validatorодного из полей, рассчитывая на порядок объявления полей в классе — в общем случае это ненадёжно; забывают, чтоmodel_validator(mode='after')в Pydantic v2 получает и должен возвращать сам экземпляр модели (self), а не словарь, в отличие отmode='before'.