Skip to content

Pydantic — Models & Fields

BaseModel Basics

from pydantic import BaseModel


class User(BaseModel):
    id: int
    name: str
    email: str
    is_active: bool = True


user = User(id=1, name="Alice", email="alice@test.com")
print(user.name)          # "Alice"
print(user.model_fields)  # field metadata dict

Models are mutable by default in v2 (frozen=False). Set ConfigDict(frozen=True) to make them immutable. Fields without defaults are required.


Field Constraints

from pydantic import BaseModel, Field


class Product(BaseModel):
    name: str = Field(min_length=1, max_length=100)
    sku: str = Field(pattern=r"^[A-Z]{2}-\d{4}$")
    price: float = Field(gt=0, le=99_999.99)
    quantity: int = Field(ge=0, default=0)
    tags: list[str] = Field(default_factory=list, max_length=10)
Param Meaning Param Meaning
gt / ge Greater than / ≥ lt / le Less than / ≤
multiple_of Divisible by value min_length Min character count
max_length Max character count pattern Regex pattern
strip_whitespace Trim spaces to_lower / to_upper Normalize case

Common Types

from datetime import date, datetime
from decimal import Decimal
from enum import Enum
from uuid import UUID

from pydantic import BaseModel, EmailStr, Field, HttpUrl


class Status(str, Enum):
    ACTIVE = "active"
    INACTIVE = "inactive"


class Account(BaseModel):
    id: UUID
    email: EmailStr                        # requires email-validator
    website: HttpUrl
    balance: Decimal
    status: Status
    created_at: datetime
    birth_date: date
    metadata: dict[str, str] = Field(default_factory=dict)
    scores: list[int] = Field(default_factory=list)
    unique_tags: set[str] = Field(default_factory=set)
Type Accepts Notes
str, int, float, bool Coerced by default Use strict mode to disable
list[T], set[T] Sequences Inner type validated
dict[K, V] Mappings Keys and values validated
tuple[int, str] / tuple[int, ...] Fixed / variable-length Positional or homogeneous
T \| None Nullable Required unless = None
UUID, datetime, date String or native Auto-parses
Decimal String, int, float Preserves precision
Enum Value or member Validates membership
EmailStr, HttpUrl String Requires email-validator / built-in

Nested Models

from pydantic import BaseModel


class Address(BaseModel):
    street: str
    city: str
    zip_code: str


class Company(BaseModel):
    name: str
    address: Address


data = {
    "name": "Acme",
    "address": {"street": "123 Main", "city": "NYC", "zip_code": "10001"},
}
company = Company.model_validate(data)
print(company.address.city)  # "NYC"

Nested models validate recursively — inner dicts are auto-parsed into model instances.


Aliases

from pydantic import BaseModel, Field


class Event(BaseModel):
    event_type: str = Field(alias="eventType")
    start_time: str = Field(alias="startTime")
    is_public: bool = Field(alias="isPublic", default=True)

event = Event.model_validate({"eventType": "talk", "startTime": "10:00"})
print(event.event_type)                   # "talk" — Python access
print(event.model_dump(by_alias=True))    # {"eventType": "talk", ...}

Validation Alias with Multiple Sources

from pydantic import AliasChoices, AliasPath, BaseModel, Field


class Config(BaseModel):
    db_host: str = Field(
        validation_alias=AliasChoices("DB_HOST", AliasPath("database", "host")),
    )

Accepts {"DB_HOST": "localhost"} or {"database": {"host": "localhost"}}.


Optional & Default Patterns

from pydantic import BaseModel, Field


class Filter(BaseModel):
    query: str                                     # required
    page: int = 1                                  # default value
    per_page: int = Field(default=25, ge=1, le=100)
    tags: list[str] | None = None                  # optional, defaults to None
    sort_by: str = Field(default="created_at")


f = Filter(query="python")
f.model_dump()                    # all fields, including defaults
f.model_dump(exclude_unset=True)  # {"query": "python"} — only set by caller
f.model_dump(exclude_none=True)   # omits None-valued fields

Use exclude_unset=True for PATCH semantics — only update fields the client explicitly sent.