This is Chapter 1 of the Pydantic v2 textbook. Pairs with the FastAPI textbook (Pydantic is FastAPI’s validator) and the SQLAlchemy textbooks .
Pydantic in one paragraph
Pydantic is a Python library that turns type hints into validation. You declare a model:
from pydantic import BaseModel
class User(BaseModel):
id: int
email: str
name: str | None = None
You parse input:
user = User.model_validate({"id": 1, "email": "[email protected]"})
Or serialize:
user.model_dump()
user.model_dump_json()
Two operations: validation and serialization. v2 made both fast (Rust core).
What changed from v1
If you’ve used v1:
BaseModelAPI renamed:parse_obj→model_validate,dict()→model_dump(),.json()→model_dump_json().Configclass →model_configdict.@validator→@field_validator;@root_validator→@model_validator.- Strict mode optional / per field.
- Discriminated unions native; faster.
- Generic models cleaner.
- Settings moved to
pydantic-settingspackage. - 5–50× faster validation (Rust core).
For 2026: write v2. v1 is in maintenance.
Architecture
Pydantic v2 is a Python frontend over a Rust core (pydantic-core). When you define a model, Pydantic compiles a schema; the Rust core uses that schema to validate at runtime.
This means:
- Validation is fast.
- Some flexibility from v1 is gone (e.g., post-init mutation patterns).
- Errors are detailed and structured.
BaseModel
from pydantic import BaseModel, Field, EmailStr
from datetime import datetime
class User(BaseModel):
id: int
email: EmailStr
name: str = Field(..., min_length=1, max_length=120)
age: int | None = Field(default=None, ge=0, le=150)
created_at: datetime = Field(default_factory=datetime.utcnow)
EmailStrvalidates email format (requiresemail-validator).Fieldadds metadata (constraints, descriptions, defaults).default_factoryfor mutable / dynamic defaults.
Validation
# From dict
user = User.model_validate({"id": 1, "email": "[email protected]", "name": "Alice"})
# From JSON
user = User.model_validate_json('{"id": 1, "email": "[email protected]", "name": "Alice"}')
# Direct
user = User(id=1, email="[email protected]", name="Alice")
User(...) calls model_validate(...) under the hood.
Validation errors
try:
User.model_validate({"id": "abc"})
except ValidationError as e:
print(e.errors())
[
{
"type": "int_parsing",
"loc": ("id",),
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "abc",
},
{
"type": "missing",
"loc": ("email",),
"msg": "Field required",
"input": {"id": "abc"},
},
]
Structured errors. Each has type, loc (path), msg, input.
Serialization
user.model_dump()
# {"id": 1, "email": "[email protected]", "name": "Alice", "age": None, "created_at": ...}
user.model_dump(exclude={"age"})
user.model_dump(exclude_none=True)
user.model_dump(mode="json") # JSON-friendly types
user.model_dump_json()
# '{"id":1,"email":"[email protected]","name":"Alice","age":null,"created_at":"..."}'
mode="json" converts datetimes to strings, etc. (vs mode="python" which keeps Python types).
Type coercion
By default, Pydantic v2 is lenient:
User.model_validate({"id": "1"}) # "1" → 1 (int)
User.model_validate({"id": 1.0}) # 1.0 → 1
User.model_validate({"age": "25"}) # "25" → 25
For strict:
class User(BaseModel):
model_config = {"strict": True}
id: int
Now "1" → ValidationError.
Or per-field via Strict types.
Why this matters
Pydantic sits between the outside world (JSON, dicts, env vars, query params) and your Python code. Validate at the boundary; once an instance exists, you can trust it.
This is the same role for FastAPI request validation, settings (config), structured output from LLMs, internal RPC, Kafka events, and more.
The textbook structure
| Ch | Topic |
|---|---|
| 1 | This chapter — introduction |
| 2 | Fields, types, and constraints |
| 3 | Validators (field, model, before, after) |
| 4 | Serialization in depth |
| 5 | Nested models, generics, discriminated unions |
| 6 | Custom types and TypeAdapter |
| 7 | Strict mode and coercion |
| 8 | JSON Schema generation |
| 9 | Settings (pydantic-settings) |
| 10 | Performance, FastAPI integration, alternatives |
Read this next
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .