Skip to content

Pydantic — Config & Performance

ConfigDict

from pydantic import BaseModel, ConfigDict


class StrictUser(BaseModel):
    model_config = ConfigDict(
        strict=True,              # no type coercion
        frozen=True,              # immutable instances
        extra="forbid",           # reject unknown fields
        str_strip_whitespace=True,
        validate_assignment=True, # re-validate on attr set
        populate_by_name=True,    # accept both alias and field name
        use_enum_values=True,     # store enum value, not member
    )

    name: str
    age: int
Option Default Effect
strict False Disable type coercion
frozen False Make model immutable (hashable)
extra "ignore" "allow", "forbid", or "ignore" unknown fields
validate_assignment False Re-validate when setting attributes
populate_by_name False Accept both field name and alias
str_strip_whitespace False Strip whitespace from strings
use_enum_values False Store enum .value instead of member
validate_default False Validate default values

Strict Mode

Prevents automatic type coercion — values must match the declared type exactly.

from pydantic import BaseModel, ConfigDict, Field, ValidationError


class Measurement(BaseModel):
    model_config = ConfigDict(strict=True)
    value: float
    unit: str


Measurement(value=3.14, unit="kg")        # OK
try:
    Measurement(value="3.14", unit="kg")  # ValidationError — str ≠ float
except ValidationError as e:
    print(e.errors()[0]["type"])           # "float_type"


class Mixed(BaseModel):
    strict_id: int = Field(strict=True)   # per-field strict
    flexible_name: str                     # coercion allowed

Pydantic Dataclasses

Validation with stdlib dataclass interface.

from pydantic import ConfigDict, field_validator
from pydantic.dataclasses import dataclass


@dataclass(config=ConfigDict(strict=True, frozen=True))
class Point:
    x: float
    y: float

    @field_validator("x", "y")
    @classmethod
    def must_be_finite(cls, v: float) -> float:
        if not (-1e6 <= v <= 1e6):
            raise ValueError("coordinate out of range")
        return v

Supports __post_init__, field_validator, model_validator, and ConfigDict.


Performance Tips

1. Use model_validate_json for JSON Input

user = User.model_validate_json(raw_json_bytes)

Faster than json.loads() + model_validate() — Rust-based parser handles both steps.

2. Direct JSON Output

json_str = user.model_dump_json()  # no intermediate dict

Prefer model_dump_json() over json.dumps(model.model_dump()).

3. Discriminated Unions Short-Circuit

from typing import Annotated, Literal, Union

from pydantic import BaseModel, Field


class Cat(BaseModel):
    pet_type: Literal["cat"]
    meow_volume: int


class Dog(BaseModel):
    pet_type: Literal["dog"]
    bark_volume: int


Pet = Annotated[Union[Cat, Dog], Field(discriminator="pet_type")]

4. Frozen Models for Caching

from functools import lru_cache

from pydantic import BaseModel, ConfigDict


class CacheKey(BaseModel):
    model_config = ConfigDict(frozen=True)
    endpoint: str
    params: tuple[tuple[str, str], ...]


@lru_cache(maxsize=128)
def fetch(key: CacheKey) -> dict:
    ...

frozen=True makes models hashable — compatible with lru_cache, sets, dict keys.


FastAPI Integration

from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI()


class UserCreate(BaseModel):
    name: str = Field(min_length=1, max_length=100)
    email: str


class UserRead(BaseModel):
    id: int
    name: str
    email: str


@app.post("/users", response_model=UserRead, status_code=201)
async def create_user(user: UserCreate) -> UserRead:
    db_user = await save_user(user)
    return UserRead.model_validate(db_user)

Pattern: Base for shared fields, Create adds write-only, Update makes all optional, Read adds read-only.


v1 → v2 Migration

v1 v2
.dict() .model_dump()
.json() .model_dump_json()
.parse_obj(d) .model_validate(d)
.parse_raw(s) .model_validate_json(s)
.schema() .model_json_schema()
@validator @field_validator
@root_validator @model_validator
Config class model_config = ConfigDict(...)
orm_mode = True from_attributes=True

Run bump-pydantic to auto-migrate most v1 code to v2 syntax.