Cheatsheet for Field() and constraints.
Common Field args
from pydantic import Field
class M(BaseModel):
name: str = Field(
..., # required (Ellipsis)
title="Name",
description="User's full name",
examples=["Alice"],
min_length=1, max_length=120,
pattern=r"^[A-Za-z ]+$",
)
age: int = Field(default=18, ge=0, le=150, multiple_of=1)
bio: str | None = Field(default=None, max_length=1000)
tags: list[str] = Field(default_factory=list, max_length=10)
secret: str = Field(..., exclude=True) # never serialized
Default vs default_factory
# BAD: shared mutable
tags: list[str] = []
# GOOD: per-instance
tags: list[str] = Field(default_factory=list)
created: datetime = Field(default_factory=datetime.utcnow)
Aliases
class User(BaseModel):
full_name: str = Field(alias="fullName")
model_config = {"populate_by_name": True}
# Accept JSON {"fullName": ...} OR {"full_name": ...}
# Separate validation / serialization aliases
class User(BaseModel):
user_id: int = Field(validation_alias="id", serialization_alias="userId")
AliasChoices / AliasPath
from pydantic import AliasChoices, AliasPath
# Multiple alias options
name: str = Field(validation_alias=AliasChoices("name", "fullName", "full_name"))
# Nested path
user_id: int = Field(validation_alias=AliasPath("data", "user", "id"))
# accepts {"data": {"user": {"id": 42}}}
Constraints by type
String
name: str = Field(..., min_length=1, max_length=120, pattern=r"^[a-z]+$")
Numeric
age: int = Field(..., gt=0, ge=1, lt=150, le=149, multiple_of=1)
Collection
tags: list[str] = Field(default_factory=list, min_length=1, max_length=10)
Annotated types (DRY)
from typing import Annotated
from pydantic import StringConstraints
Username = Annotated[str, StringConstraints(
min_length=3, max_length=32,
pattern=r"^[a-z0-9_]+$",
strip_whitespace=True,
)]
class User(BaseModel):
username: Username
Reuse across models.
Documentation in OpenAPI
class User(BaseModel):
email: EmailStr = Field(..., description="Email address", examples=["[email protected]"])
age: int = Field(..., ge=0, examples=[25, 30, 45])
model_config = {
"json_schema_extra": {
"examples": [
{"email": "[email protected]", "age": 30},
]
}
}
Read-only / write-only
class User(BaseModel):
id: int = Field(..., exclude=True) # never out
password: str = Field(..., init=False) # not in __init__ (advanced)
For API: use separate Read/Write models.
SecretStr / SecretBytes
from pydantic import SecretStr
class Config(BaseModel):
api_key: SecretStr
c = Config(api_key="abc")
print(c) # api_key=SecretStr('**********')
print(c.api_key.get_secret_value()) # "abc"
Prevents accidental logging.
Json type
from pydantic import Json
class M(BaseModel):
payload: Json[dict] # accepts JSON string; parses; validates as dict
File types
from pydantic import FilePath, DirectoryPath, NewPath
class Settings(BaseModel):
config_file: FilePath # must exist
output_dir: DirectoryPath # must exist
log_file: NewPath # must NOT exist
URL types
from pydantic import HttpUrl, AnyUrl, PostgresDsn
class Config(BaseModel):
base_url: HttpUrl
db_url: PostgresDsn
Datetime parsing
from datetime import datetime, date, time
from pydantic import AwareDatetime, NaiveDatetime
class Event(BaseModel):
when: AwareDatetime # timezone-aware required
when_naive: NaiveDatetime # timezone-naive
on: date
at: time
Common mistakes
Field(default=[])— shared mutable; usedefault_factory.- Forgetting
populate_by_namewhen using aliases. - Putting validators inside Field — use
field_validatorinstead. - Strict mode globally — query params arrive as strings.
Read this next
If you want my Annotated-types library, it’s at rajpoot.dev .
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 .