Pydantic automatically coerces and validates input types
When creating a Pydantic model instance, input data is automatically coerced to the declared field types. For example, the string '123' is converted to integer 123, the datetime string '2017-06-01 12:22' is parsed to a datetime object, and list items '2' and b'3' are converted to integers 2 and 3.
ORM object validation example with SQLAlchemy
Example of validating a SQLAlchemy ORM object into a Pydantic model: Create a Pydantic model with from_attributes=True config, then call model_validate(sql_model) to convert an ORM instance to the Pydantic model. When dumping with model_dump(), field names are used by default; use model_dump(by_alias=True) to use aliases in the output.
ORM validation failures from historical data
When validating ORM objects, validation can fail on rows written before a database constraint was added or on columns that allow NULL where the Pydantic model does not. These failures only surface when the offending row is actually read from the database, which may be long after a deployment. Recording failed validations with their structured errors and rejected values helps identify problematic data.
EmailStr field type for email validation
Pydantic provides an EmailStr field type that validates email addresses. It can be used in model definitions like email: EmailStr to ensure email fields contain valid email addresses.
Field constraints no longer pushed to generic parameters
In Pydantic V2, field constraints are no longer automatically pushed down to generic parameters. Instead of `my_list: list[str] = Field(pattern=".*")`, use `my_list: list[Annotated[str, Field(pattern=".*")]]`.
Type coercion changes in V2: numbers to strings optional
In Pydantic V2, coercing int, float, and Decimal values to strings is now optional and disabled by default. Use the `coerce_numbers_to_str` config setting to enable it.
Iterable of pairs no longer coerced to dict
In Pydantic V2, an iterable of pairs is no longer coerced to a dict.
Iterables of pairs no longer validate for dict type
In Pydantic V2, iterables of pairs (which include empty iterables) no longer pass validation for fields of type dict.
Union type preserves input type when possible
In Pydantic V2, union types preserve the type of the input whenever possible, even if the correct type is not the first choice for which the input would pass validation. For example, `Model(x='1')` with annotation `x: int | str` returns `x='1'` (string), not `x=1` (int). To revert to left-to-right validation, use `Field(union_mode='left_to_right')`.
Optional field required-ness changed in V2
In Pydantic V2, a field annotated as `Optional[T]` is required and allows `None`, but does not have a default value of `None` by default. This is a breaking change from V1. Use `Optional[T] = None` for an optional field with None default.
Any field no longer defaults to None in V2
In Pydantic V2, fields annotated as `Any` no longer have a default value of `None`. A field with annotation `Any` is required unless given an explicit default.
V2 required optional and nullable fields behavior
In Pydantic V2, the following field behaviors apply: `f1: str` is required, cannot be None; `f2: str = 'abc'` is not required, cannot be None, defaults to 'abc'; `f3: Optional[str]` is required, can be None; `f4: Optional[str] = None` is not required, can be None, defaults to None; `f5: Optional[str] = 'abc'` is not required, can be None, defaults to 'abc'; `f6: Any` is required, can be any type; `f7: Any = None` is not required, can be any type, defaults to None.
Pydantic V2 uses Rust regex crate instead of Python regex
In Pydantic V2, regex patterns on strings use the Rust regex crate instead of Python's regex library. The Rust crate promises linear time searching but does not support lookarounds or backreferences. Use the `regex_engine` config setting to revert to Python's regex library if needed.
Float to int conversion stricter in V2
In Pydantic V2, type conversion from floats to integers is only allowed if the decimal part is zero. In V1, any float was accepted for int fields. For example, `Model(x=10.2)` raises a ValidationError with type `int_from_float`.
Custom type __get_validators__ replaced with __get_pydantic_core_schema__
In Pydantic V2, the `__get_validators__` method for custom types is replaced with `__get_pydantic_core_schema__`, which provides access to pydantic-core schema generation for better performance.
Use Annotated to customize type schemas in V2
In Pydantic V2, you can use `typing.Annotated` to add `__get_pydantic_core_schema__` and `__get_pydantic_json_schema__` functions to types by annotating them, rather than modifying the type itself. This provides a flexible mechanism for integrating third-party types.
Color and Payment Card types moved to pydantic-extra-types
In Pydantic V2, Color types and Payment Card Number types have been moved to the pydantic-extra-types package and are no longer part of core Pydantic.
URL types no longer inherit from str in V2
In Pydantic V2, the URL and DSN types (like AnyUrl) no longer inherit from str. They are built using Annotated. To use them in APIs expecting str, convert with `str(url)`.
Pydantic V2 uses Rust URL crate for URL validation
In Pydantic V2, URL validation uses Rust's Url crate. Some URL validation behavior differs from V1, notably that slashes are appended to validated URLs if no path is included.
URL slash appending behavior in V2
In Pydantic V2, AnyUrl appends slashes to the validated version if no path is included. For example, `AnyUrl(url='https://google.com')` returns `'https://google.com/'`, but `AnyUrl(url='https://google.com/api')` returns `'https://google.com/api'` without adding a slash.
Constrained types removed, use Annotated with Field
In Pydantic V2, the Constrained* classes (ConstrainedInt, ConstrainedStr, etc.) have been removed. Replace them with `Annotated[<type>, Field(...)]`. For example, `ConstrainedInt(ge=0)` becomes `Annotated[int, Field(ge=0)]`.
StringConstraints for replacing ConstrainedStr
In Pydantic V2, for ConstrainedStr, use StringConstraints instead.