FastAPI: From Basics to Production · Pydantic v2 in Depth
Field Constraints and Validators
Validate data with Field constraints, field validators and model validators.
Pydantic v2 (rewritten in Rust, much faster than v1) validates with type hints plus Field constraints (min_length, ge, pattern). For custom rules use @field_validator (one field) and @model_validator(mode="after") (cross-field checks). Validators should raise ValueError with clear messages; FastAPI turns them into 422 responses with field locations.
from datetime import date
from typing import Self
from pydantic import BaseModel, Field, field_validator, model_validator
class BookingIn(BaseModel):
guest_name: str = Field(min_length=2, max_length=100)
check_in: date
check_out: date
guests: int = Field(ge=1, le=8)
promo_code: str | None = None
@field_validator("promo_code")
@classmethod
def normalize_code(cls, v: str | None) -> str | None:
return v.strip().upper() if v else None
@model_validator(mode="after")
def check_dates(self) -> Self:
if self.check_out <= self.check_in:
raise ValueError("check_out must be after check_in")
return self
Common pitfalls
- Using v1 decorators (
@validator,@root_validator) — deprecated in v2. - Forgetting
@classmethodon field validators. - Doing I/O (database lookups) inside validators.
Key points
- Field constraints for simple rules
- field_validator / model_validator for custom rules
- Keep validators pure
Further reading: Pydantic: Validators
Practice questions
Where should a check that end_date > start_date live?
Answer
In a @model_validator(mode='after'), since it involves multiple fields and needs both validated values.
Why avoid database queries inside Pydantic validators?
Answer
Validators should be pure and synchronous; I/O makes them slow, hard to test and can't use async sessions. Do such checks in the endpoint or service layer.
Open in the app to mark lessons complete, take notes and track your progress.