Опирается на правила:
R-VLD-STD-1…R-VLD-STD-5иR-VLD-STD-X1…R-VLD-STD-X3из Validation Style Guide → раздел 2. Стандартные constraints.
Важно знать
- Required vs optional — через тип, не через
Field(default=...):name: str(required),note: str | None = None(optional). Pydantic 2 строго различает.- Пустая строка —
Field(min_length=1). Типstrне запрещает"".- Числа —
Field(ge=, le=, gt=, lt=). Деньги —Decimal, неfloat.- Формат email —
EmailStrизpydantic[email], неField(pattern=...)с самописным regex.- UUID, AnyUrl, PostgresDsn — специальные типы Pydantic; никакого regex вручную.
- Время —
datetime/dateиз stdlib; ограничения «не в прошлом» — через@field_validator.@field_validator«не None» на поле без| None— бессмысленно: Pydantic уже гарантирует.
Pydantic v2 покрывает 80% типичных валидаций входных DTO. Стандартные средства — контракт, который одинаково читают разработчик, ревьюер и codegen. Самописный велосипед вместо EmailStr ломает эту общую базу. Раскрытие раздела 2 гайда.
Required vs optional
R-VLD-STD-1: разграничение через систему типов, не через флаги.
from pydantic import BaseModel, Field
from uuid import UUID
from datetime import date
class CreateCustomerRequest(BaseModel):
first_name: str = Field(min_length=1, max_length=100)
last_name: str = Field(min_length=1, max_length=100)
email: EmailStr
phone: str | None = None
birth_date: date | None = None
first_name: str— required. Pydantic откажет, если поле отсутствует в теле.phone: str | None = None— optional.None— валидное значение.Field(min_length=1)— защита от пустой строки. Типstrразрешает"".
Аналог @NotBlank из Jakarta: str + Field(min_length=1). Аналог @NotNull для объектов: тип без | None.
Размеры — Field с числовыми параметрами
R-VLD-STD-2: строки — min_length/max_length, числа — ge/le/gt/lt.
from decimal import Decimal
from pydantic import BaseModel, Field
class OrderItemRequest(BaseModel):
sku: str = Field(min_length=1, max_length=64)
quantity: int = Field(ge=1, le=9999)
unit_price: Decimal = Field(gt=Decimal("0"), decimal_places=2)
| Параметр | Значение |
|---|---|
ge | greater or equal (≥) |
le | less or equal (≤) |
gt | greater than (>) |
lt | less than (<) |
min_length | для str и list |
max_length | для str и list |
min_length на list | аналог @NotEmpty + @Size(min=1) |
Формат — специальные типы, не regex
R-VLD-STD-3: стандартные форматы — стандартными средствами.
from pydantic import BaseModel, EmailStr, AnyUrl
from uuid import UUID
class CreateCustomerRequest(BaseModel):
customer_id: UUID
email: EmailStr
site: AnyUrl | None = None
Типы Pydantic, охватывающие стандартные форматы:
| Тип | Что валидирует |
|---|---|
EmailStr | email по RFC 5321 |
AnyUrl | URL (scheme + host) |
AnyHttpUrl | HTTP/HTTPS URL |
PostgresDsn | PostgreSQL DSN |
UUID | UUID v1-v5 |
IPvAnyAddress | IPv4 или IPv6 |
R-VLD-STD-X2: Field(pattern=r"^[^@]+@[^@]+\.[^@]+$") вместо EmailStr — антипаттерн. Самописный regex устаревает, не учитывает corner cases RFC, требует поддержки.
Деньги — Decimal, не float
R-VLD-STD-5: финансовые суммы — Decimal с явным decimal_places.
from decimal import Decimal
from pydantic import BaseModel, Field
class CreateOrderRequest(BaseModel):
total_amount: Decimal = Field(gt=0, decimal_places=2)
discount: Decimal = Field(ge=0, le=Decimal("100"), decimal_places=2)
float для денег — антипаттерн: потеря точности при арифметике (0.1 + 0.2 != 0.3). Decimal с decimal_places=2 гарантирует, что клиент не передаст 99.999.
Для конфига и внутренних числовых параметров float допустим — там потеря точности не имеет финансовых последствий.
Время — datetime и @field_validator
R-VLD-STD-4: Pydantic принимает ISO 8601 строки и парсит их в datetime/date автоматически. Ограничения «не в прошлом» — через @field_validator.
from datetime import date
from pydantic import BaseModel, field_validator
import datetime as dt
class CreateContractRequest(BaseModel):
start_date: date
end_date: date
@field_validator("start_date")
@classmethod
def start_not_in_past(cls, v: date) -> date:
if v < dt.date.today():
raise ValueError("Дата начала не может быть в прошлом")
return v
Строку "2026-07-01" Pydantic разберёт сам. Некорректный формат "01/07/2026" → ValidationError без дополнительного кода.
Что запрещено
| Антипаттерн | Правило | Что взамен |
|---|---|---|
@field_validator «не None» на поле без \| None | R-VLD-STD-X1 | Тип str уже гарантирует; убрать валидатор |
Field(pattern=email_regex) вместо EmailStr | R-VLD-STD-X2 | EmailStr из pydantic[email] |
float для денежных сумм | R-VLD-STD-5 | Decimal = Field(..., decimal_places=2) |
Один «всё-в-одном» @field_validator с 5 проверками | R-VLD-STD-X3 | Отдельные Field(...) параметры + специальные типы |
str = Field(default=None) для optional | R-VLD-STD-1 | str \| None = None |
Куда дальше
- Validation → раздел 2. Стандартные constraints — нормативные формулировки
R-VLD-STD-*. - python/custom-constraints —
Annotated[T, AfterValidator(...)]для частых доменных форматов. - python/cross-field-validation —
@model_validatorдля правил между полями. - python/messages-and-i18n — тексты ошибок на русском без технических терминов.