pip install pydanticv2. Settings & email are extras:pydantic-settings,pydantic[email].from pydantic import BaseModel★The base class everything subclasses.class User(BaseModel):★
id: intAnnotate fields; the schema is built at class creation.name: str = "anon"A value makes the field optional (with a default).User(id=1)★Instantiate = validate. Bad data raisesValidationError.user.model_fieldsIntrospect fields ·model_fields_set= which were provided.
User(**data)★Keyword args — the everyday path.User.model_validate(data_dict)★From a dict (or any mapping).User.model_validate_json(json_str)★Parse + validate JSON in one Rust-fast step.User.model_validate(orm, from_attributes=True)Read attributes off an ORM/arbitrary object.User.model_validate(d, context={…})Pass runtime context to validators.User.model_construct(**d)unsafeBuild without validation — trusted data only.
u.model_dump()★→ dict (keepsdatetime/UUIDas objects).u.model_dump(mode='json')★→ dict of JSON-safe primitives.u.model_dump_json()★→ JSON string directly..model_dump(exclude={'id'})include=/exclude=pick fields..model_dump(exclude_unset=True)★Only fields that were set — perfect for PATCH..model_dump(by_alias=True)exclude_none,exclude_defaults,round_triptoo.
x: int = Field(default=0)★Default + metadata in one place.Field(default_factory=list)★For mutable defaults — never= [].Field(gt=0, le=120)★Numbers:gt ge lt le multiple_of.Field(min_length=1, max_length=50)Strings/collections;pattern=for regex.Field(max_digits=5, decimal_places=2)Decimal precision.Field(description=…, examples=[…])Feeds JSON Schema ·frozen,exclude,repr.
x: Annotated[int, Field(gt=0)]★Preferred overx: int = Field(...)— composable & reusable.Pos = Annotated[int, Field(gt=0)]Name it once, reuse across models.Annotated[str, StringConstraints(…)]Bundled string rules (strip, case, pattern).Annotated[int, AfterValidator(f)]Attach validators/serializers to the type itself.
str · int · float · bool · bytes★Scalars, coerced by default (lax mode).list[int] · dict[str,int] · set · tuple★Parametrized containers validate their items.x: int | None = None★Optional value;Union[a,b]/a | bfor either.datetime · date · time · timedeltaParsed from ISO strings & timestamps.Decimal · UUID · Path · EnumRich stdlib types work out of the box.Literal['a', 'b']Restrict to exact values; nest models freely.
EmailStr · HttpUrl · AnyUrl★Validated email/URL (pip install pydantic[email]).SecretStr · SecretBytesMasked inrepr/logs; read via.get_secret_value().PositiveInt · NonNegativeIntNamed constraints, noFieldneeded.conint(gt=0) · constr(pattern=…) · conlistConstrained-type factories (Annotated is preferred).PostgresDsn · IPvAnyAddress · JsonDSNs, IPs, andJson[T]= parse a JSON string field.
@field_validator('email')★
@classmethod
def f(cls, v): ...Must be a@classmethod; return the (fixed) value.@field_validator('x', mode='before')★before= raw input;after(default) = post-coercion.def f(cls, v, info): info.dataValidationInfoexposes other fields &info.context.@field_validator('a', 'b')One validator for many fields ·'*'= all fields.raise ValueError('bad')RaiseValueError/AssertionErrorto fail validation.
@model_validator(mode='after')★
def f(self): return selfInstance method (usesself); must return self.@model_validator(mode='before')
@classmethodSees raw input dict before field parsing.if self.a > self.b: raise ...Cross-field checks live here, not infield_validator.mode='wrap'Full control: call (or skip) the inner handler.
Annotated[int, BeforeValidator(f)]Runs before core validation.Annotated[str, AfterValidator(f)]★Runs after — the common case.WrapValidator(f)Wraps core validation with a handler you call.PlainValidator(f)Replaces core validation entirely.
@field_serializer('amount')★
def f(self, v): ...Customize one field's dumped form.@model_serializerTake over the whole model's output shape.Annotated[T, PlainSerializer(f)]Serializer on the type ·WrapSerializerto wrap default.field_serializer('x', when_used='json')Only when dumping to JSON (vs'always').
@computed_field★
@property
def area(self) -> float: ...Appears inmodel_dump()& JSON Schema.@computed_field(repr=False)Derived from other fields; not an input.obj.areaRead like a normal property; cached if you add@cached_property.
model_config = ConfigDict(…)★v2 replaces the v1 innerclass Config.extra='forbid'★'ignore'(default),'allow', or'forbid'unknown keys.frozen=TrueImmutable + hashable instances.str_strip_whitespace=TrueAuto-clean strings ·str_to_lower,str_min_length.validate_assignment=True★Re-validate on attribute set (off by default).from_attributes=TrueORM mode ·use_enum_values,populate_by_name,strict.
Field(alias='userId')★Applies to both input & output by default.Field(validation_alias='in', serialization_alias='out')Different names each direction.AliasChoices('a', 'b')Accept any of several input keys.AliasPath('x', 0, 'y')Pull from a nested path in the input.ConfigDict(populate_by_name=True)★Accept both the field name and its alias.
ta = TypeAdapter(list[int])★Wrap any type — noBaseModelneeded.ta.validate_python(["1","2"])★→[1, 2]· alsovalidate_json().ta.dump_python(obj) / ta.dump_json(obj)Serialize arbitrary types the same way.ta.json_schema()Schema for any type; build the adapter once & reuse.
class Tags(RootModel[list[str]]):★Model whose root is a list/scalar, not a dict.Tags(['a','b']).rootAccess the wrapped value via.root.create_model('M', x=(int, ...))Build a model class at runtime.class Outer(BaseModel): inner: InnerNest models freely; validation recurses.
except ValidationError as e:★Raised on bad input; catch it around validation.e.errors()★List of{type, loc, msg, input}·e.error_count(),e.json().n: int # lax: "5" → 5Coercion is on by default.ConfigDict(strict=True)★Forbid coercion model-wide ·Field(strict=True)per field.x: StrictInt · StrictStrStrict types inline ·Strict()inAnnotated.
from pydantic_settings import BaseSettings★Separate package:pip install pydantic-settings.class Cfg(BaseSettings): port: int = 8000Auto-reads env vars into fields.SettingsConfigDict(env_prefix='APP_', env_file='.env')Prefix +.envsupport; layered sources.@validate_call★
def f(x: int): ...Validate a function's args from its type hints.from pydantic.dataclasses import dataclassA validating drop-in for@dataclass.
.dict() .json() → model_dump[_json]()renamedAll I/O methods are nowmodel_*.@validator → @field_validatorrenamed@root_validator→@model_validator.class Config → model_configrenamedNow aConfigDictassignment.Optional[x] ≠ optionaltrapIt only allowsNone; add a default to make it optional.from pydantic import v1The v1 API stays importable during migration.pip install bump-pydanticAutomated codemod for most v1→v2 changes.