Field() function purpose and usage patterns
The Field() function is used to provide metadata and constraints to model fields. It can be used in two patterns: the assignment form (field: type = Field(...)) and the annotated pattern (field: Annotated[type, Field(...)] = default). The annotated pattern has advantages: it avoids confusion about default values and allows providing multiple metadata elements. However, the assignment form should be used for metadata that has meaning for static type checkers, including alias, default, and default_factory.
Field-specific vs type-specific metadata in Field()
Field() metadata falls into two categories: field-specific metadata (such as deprecated, alias) that only has meaning when attached to a field, and type-specific metadata (such as constraints like gt, max_length, or JSON Schema metadata like description, title) that affects validation and schema generation. Field-specific metadata can only be used on the top-level type in an Annotated expression, not nested within unions.
Field-specific metadata in Annotated must apply to whole union
When using field-specific metadata like deprecated with Annotated in a union type, the metadata must be applied to the entire union, not to the non-None part. The pattern field_ok: Annotated[int | None, Field(deprecated=True)] = None is correct, while field_bad: Annotated[int, Field(deprecated=True)] | None = None is incorrect.
Prefer built-in validation constraints over custom validators
Use built-in validation constraints through Field() or annotated_types (e.g., Gt(1) for greater than comparison) instead of defining custom field validators when possible. This approach is cleaner and more maintainable than writing @field_validator decorated methods.
StringConstraints for string-specific constraints
String constraints such as strip_whitespace, to_upper, to_lower, and ascii_only can only be specified using pydantic.StringConstraints in an Annotated pattern. These constraints cannot be expressed using the Field() function.
Field assignment form vs annotated pattern example
Assignment form: class User(BaseModel): first_name: str = Field(alias='name'). Annotated form: class Model(BaseModel): value: Annotated[int, Field(deprecated=True)] = 1. The annotated pattern avoids confusion about default values and allows multiple metadata elements.
Built-in constraints example with annotated_types
Example using built-in constraints: from annotated_types import Gt; class Model(BaseModel): constrained_int_ok: Annotated[int, Gt(1)]. This is preferable to using @field_validator with manual validation logic.
StringConstraints example for string validation
Example using StringConstraints: from typing import Annotated; from pydantic import BaseModel, StringConstraints; class Model(BaseModel): a: Annotated[str, StringConstraints(strip_whitespace=True)]. This applies string-specific constraints that cannot be expressed using Field().
Field function with gt parameter
The Field function accepts a gt parameter to specify that a numeric value must be greater than the specified value. Example: Field(description='Estimated population', gt=0) enforces that the population value must be greater than 0.
Field-level JSON schema customization parameters
Field parameters used exclusively to customize generated JSON Schema are: title (the title of the field), description (the description of the field), examples (the examples of the field), json_schema_extra (extra JSON Schema properties to be added to the field, accepts dict or Callable), and field_title_generator (a function that programmatically sets the field's title, based on its name and info).
PlainSerializer and WrapSerializer annotations
PlainSerializer and WrapSerializer are used with the Annotated pattern to define field serializers. PlainSerializer takes a callable that returns the serialized value. WrapSerializer takes a callable with a mandatory handler parameter (SerializerFunctionWrapHandler) that allows running code before/after Pydantic serialization logic.
PlainSerializer example with Annotated
from typing import Annotated, Any
from pydantic import BaseModel, PlainSerializer
def ser_number(value: Any) -> Any:
if isinstance(value, int):
return value * 2
else:
return value
class Model(BaseModel):
number: Annotated[int, PlainSerializer(ser_number)]
print(Model(number=4).model_dump())
#> {'number': 8}
WrapSerializer example with Annotated
from typing import Annotated, Any
from pydantic import BaseModel, SerializerFunctionWrapHandler, WrapSerializer
def ser_number(value: Any, handler: SerializerFunctionWrapHandler) -> int:
return handler(value) + 1
class Model(BaseModel):
number: Annotated[int, WrapSerializer(ser_number)]
print(Model(number=4).model_dump())
#> {'number': 5}
Reusable type with PlainSerializer via Annotated
from typing import Annotated
from pydantic import BaseModel, Field, PlainSerializer
DoubleNumber = Annotated[int, PlainSerializer(lambda v: v * 2)]
class Model1(BaseModel):
my_number: DoubleNumber
class Model2(BaseModel):
other_number: Annotated[DoubleNumber, Field(description='My other number')]
class Model3(BaseModel):
list_of_even_numbers: list[DoubleNumber]
SerializeAsAny annotation for field-level duck typing
SerializeAsAny is used to annotate a field type for duck-typed serialization behavior at the field level. Validation behavior remains the same as the wrapped type, static type checkers treat it as the wrapped type, but during serialization the field is serialized as though typed as Any. Syntax: SerializeAsAny[<type>].
Field-level exclude and exclude_if parameters
The Field() function accepts exclude (bool) and exclude_if (callable) parameters for field-level serialization control. exclude=True permanently excludes a field from serialization. exclude_if takes a callable that receives the field value and returns True to exclude the field. Field-level exclusion takes priority over the include serialization parameter.
Field exclude and exclude_if example
from pydantic import BaseModel, Field
class Transaction(BaseModel):
id: int
private_id: int = Field(exclude=True)
value: int = Field(ge=0, exclude_if=lambda v: v == 0)
print(Transaction(id=1, private_id=2, value=0).model_dump())
#> {'id': 1}
Field() function signature and basic usage
The Field() function is used to customize Pydantic model fields and behaves the same way as the standard library field() function for dataclasses. It is assigned to an annotated attribute. Example: class Model(BaseModel): name: str = Field(frozen=True). The ellipsis (...) can be used to explicitly mark a required field, but its usage is discouraged as it does not work well with static type checkers.
Field() parameters for default values
The Field() function accepts 'default' and 'default_factory' parameters. The 'default' parameter sets a static default value. The 'default_factory' parameter accepts a callable that generates a default value. Starting in v2.10, default factories can accept a single required argument containing already validated data as a dictionary, which will only contain validated data based on field order.
Field() constraint parameters
Field() can be used to add constraints to specific types. Common constraint parameters include: gt (greater than), lt (less than), max_length, min_length, max_digits, decimal_places, and others depending on the type. When adding constraints to a union type containing None or MISSING, constraints are automatically applied to the remaining type(s).
Field() strict mode parameter
The 'strict' parameter of Field() specifies whether the field should be validated in strict mode. Default value is False (lax mode). Example: name: str = Field(strict=True) enforces strict validation while age: int = Field(strict=False) allows coercion.
Field() dataclass-specific parameters
Field() supports three dataclass-specific parameters: 'init' (whether the field should be included in the synthesized __init__() method), 'init_var' (whether the field should be init-only in the dataclass), and 'kw_only' (whether the field should be a keyword-only argument in the constructor).
Field() immutability with frozen parameter
The 'frozen' parameter prevents a field from being assigned a new value after the model is created. When attempting to modify a frozen field, a ValidationError is raised with type=frozen_field. Example: name: str = Field(frozen=True).
Field() representation control with repr parameter
The 'repr' parameter controls whether the field should be included in the string representation of the model. Default value is True. Example: name: str = Field(repr=True) includes the field in repr, age: int = Field(repr=False) excludes it.
Field() discriminator parameter for unions
The 'discriminator' parameter controls which field is used to discriminate between different models in a union. It accepts either a field name as a string or a Discriminator instance. The Discriminator approach is useful when discriminator field names differ across union members.
Field() exclusion parameters
The 'exclude' and 'exclude_if' parameters control which fields are excluded from model exports. When exclude=True, the field is not included in model_dump() output. The exclude_if parameter was added in v2.12.
Field() deprecated parameter options
The 'deprecated' parameter marks a field as deprecated and accepts three forms: (1) a string used as the deprecation message, (2) an instance of @warnings.deprecated decorator, or (3) a boolean value. When deprecated, a runtime warning is emitted on field access and the deprecated keyword is set in generated JSON schema.
validate_default field parameter
The 'validate_default' field parameter enables validation of default values. By default, Pydantic does not validate default values. When validate_default=True is set, default values are validated according to the field's type and constraints. This can also be configured at the model level via ConfigDict.validate_default.
Mutable default values handling
When a default value is not hashable, Pydantic automatically creates a deep copy of the default value for each instance of the model. This prevents the common Python bug of shared mutable default objects across instances, unlike dataclasses which raise an error in this case.
Annotated pattern for Field() metadata
The Annotated typing construct can be used to attach Field() functions and other metadata to annotations: Annotated[str, Field(strict=True), WithJsonSchema({'extra': 'data'})]. This pattern allows multiple metadata elements per field and avoids confusion about default values. However, default, default_factory, and alias arguments are only understood by static type checkers when using the normal assignment form, not the Annotated pattern.
Constraints on union type members
When adding constraints to a union type using Annotated with Field(), the constraints apply to the top-level union. If a member is None or MISSING, constraints are automatically applied only to remaining types. Be careful to place Field() on the top-level union, not on individual type members.
Field() with default_factory receiving validated data
from pydantic import BaseModel, EmailStr, Field
class User(BaseModel):
email: EmailStr
username: str = Field(default_factory=lambda data: data['email'])
user = User(email='user@example.com')
print(user.username)
#> user@example.com
The data argument contains only already-validated fields based on field order, so the example would fail if username were defined before email.
Field() with Discriminator instance
from typing import Annotated, Literal
from pydantic import BaseModel, Discriminator, Field, Tag
def pet_discriminator(v):
if isinstance(v, dict):
return v.get('pet_type', v.get('pet_kind'))
return getattr(v, 'pet_type', getattr(v, 'pet_kind', None))
class Model(BaseModel):
pet: Annotated[Cat, Tag('cat')] | Annotated[Dog, Tag('dog')] = Field(
discriminator=Discriminator(pet_discriminator)
)
Field() with deprecated string parameter
from typing import Annotated
from pydantic import BaseModel, Field
class Model(BaseModel):
deprecated_field: Annotated[int, Field(deprecated='This is deprecated')]
print(Model.model_json_schema()['properties']['deprecated_field'])
#> {'deprecated': True, 'title': 'Deprecated Field', 'type': 'integer'}
Deprecated field access emits runtime warning
When a field is marked with deprecated=True or deprecated='message', accessing the field at runtime emits a DeprecationWarning. Inside validators accessing deprecated fields, use warnings.catch_warnings() with warnings.simplefilter('ignore', DeprecationWarning) to suppress the warning.
PrivateAttr function for private attributes
PrivateAttr() is used to define private attributes on models. Attributes whose name has a leading underscore are not treated as fields by Pydantic. They are not validated or set during calls to __init__, model_validate, etc. PrivateAttr accepts default and default_factory parameters. As of v2.13, default factories can take the validated model data as an argument.
Field function for customization
Fields can be customized using the Field() function. This allows specifying default values, aliases, and other field-specific constraints and metadata. Fields can be used in model_validate() via the extra parameter.
Private attribute dunder names not supported
Private attribute names must start with underscore to prevent conflicts with model fields. However, dunder names (such as __attr__) are not supported and will be completely ignored from the model definition.