Four types of field validators
Field validators can be one of four types: after validators run after Pydantic's internal validation and are generally more type safe; before validators run before Pydantic's internal parsing and validation but handle raw input; plain validators terminate validation immediately after returning with no further validators called and no Pydantic internal validation; wrap validators are the most flexible and can run code before or after Pydantic validation or terminate immediately.
Field validator definition patterns
Field validators can be defined using two patterns: the annotated pattern with AfterValidator, BeforeValidator, PlainValidator, or WrapValidator in a type annotation, or the decorator pattern using the @field_validator decorator applied as a class method.
After validator returns validated value
After validators must return the validated value. They run after Pydantic's internal validation, check for specific conditions, and can perform coercion or mutation on the validated value.
Before validator receives raw input
Before validators receive raw input which can be any arbitrary object. The value returned from a before validator is then validated against the provided type annotation by Pydantic. Before validators run before Pydantic's internal parsing and coercion.
Plain validator terminates validation
Plain validators act similarly to before validators but terminate validation immediately after returning. No further validators are called and Pydantic does not perform any of its internal validation against the field type.
Wrap validator has mandatory handler parameter
Wrap validators must be defined with a mandatory extra handler parameter, which is a callable taking the value to be validated. The handler delegates validation of the value to Pydantic. The handler can be wrapped in a try-except block or not called at all.
Field validator decorator mode parameter
The @field_validator decorator accepts a mode parameter which can be 'after' (the default), 'before', 'plain', or 'wrap' to specify which type of field validator to use.
Field validator after mode example
Example of an after validator using the decorator pattern that checks if a number is even:
```python
from pydantic import BaseModel, ValidationError, field_validator
class Model(BaseModel):
number: int
@field_validator('number', mode='after')
@classmethod
def is_even(cls, value: int) -> int:
if value % 2 == 1:
raise ValueError(f'{value} is not an even number')
return value
try:
Model(number=1)
except ValidationError as err:
print(err)
```
Field validator before mode example
Example of a before validator using the decorator pattern that ensures input is a list:
```python
from typing import Any
from pydantic import BaseModel, ValidationError, field_validator
class Model(BaseModel):
numbers: list[int]
@field_validator('numbers', mode='before')
@classmethod
def ensure_list(cls, value: Any) -> Any:
if not isinstance(value, list):
return [value]
else:
return value
print(Model(numbers=2))
#> numbers=[2]
```
Field validator plain mode example
Example of a plain validator using the decorator pattern that doubles integers:
```python
from typing import Any
from pydantic import BaseModel, field_validator
class Model(BaseModel):
number: int
@field_validator('number', mode='plain')
@classmethod
def val_number(cls, value: Any) -> Any:
if isinstance(value, int):
return value * 2
else:
return value
print(Model(number=4))
#> number=8
print(Model(number='invalid'))
#> number='invalid'
```
Note that although 'invalid' should not validate against the int type, Pydantic accepts the input because plain validators terminate validation immediately.
Field validator wrap mode example
Example of a wrap validator using the decorator pattern that truncates strings exceeding max_length:
```python
from typing import Any, Annotated
from pydantic import BaseModel, Field, ValidationError, ValidatorFunctionWrapHandler, field_validator
class Model(BaseModel):
my_string: Annotated[str, Field(max_length=5)]
@field_validator('my_string', mode='wrap')
@classmethod
def truncate(cls, value: Any, handler: ValidatorFunctionWrapHandler) -> str:
try:
return handler(value)
except ValidationError as err:
if err.errors()[0]['type'] == 'string_too_long':
return handler(value[:5])
else:
raise
print(Model(my_string='abcde'))
#> my_string='abcde'
print(Model(my_string='abcdef'))
#> my_string='abcde'
```
Annotated pattern validator reusability
A key benefit of using the annotated pattern for validators is to make validators reusable by defining a type alias with validators, which can then be used across multiple model fields and even nested within collection types.
Annotated pattern validator example
Example of creating a reusable EvenNumber type using the annotated pattern:
```python
from typing import Annotated
from pydantic import AfterValidator, BaseModel
def is_even(value: int) -> int:
if value % 2 == 1:
raise ValueError(f'{value} is not an even number')
return value
EvenNumber = Annotated[int, AfterValidator(is_even)]
class Model1(BaseModel):
my_number: EvenNumber
class Model2(BaseModel):
other_number: Annotated[EvenNumber, AfterValidator(lambda v: v + 2)]
class Model3(BaseModel):
list_of_even_numbers: list[EvenNumber]
```
Decorator pattern multi-field validator
A key benefit of using the decorator pattern for validators is to apply a single validator function to multiple fields by passing multiple field names to the @field_validator decorator.
Example:
```python
from pydantic import BaseModel, field_validator
class Model(BaseModel):
f1: str
f2: str
@field_validator('f1', 'f2', mode='before')
@classmethod
def capitalize(cls, value: str) -> str:
return value.capitalize()
```
Field validator wildcard and check_fields
When using the @field_validator decorator, passing '*' as the field name applies the validator to all fields including ones defined in subclasses. The check_fields parameter can be set to False to disable the check that provided field names are defined on the model, which is useful when the field validator is defined on a base class and the field is expected on subclasses.
Three types of model validators
Model validators can be one of three types: after validators run after the whole model has been validated and are defined as instance methods returning the validated instance; before validators run before the model is instantiated and handle raw input; wrap validators are the most flexible and can run code before or after Pydantic validation or terminate immediately.
Model after validator example
Example of a model after validator that checks two fields match:
```python
from typing_extensions import Self
from pydantic import BaseModel, model_validator
class UserModel(BaseModel):
username: str
password: str
password_repeat: str
@model_validator(mode='after')
def check_passwords_match(self) -> Self:
if self.password != self.password_repeat:
raise ValueError('Passwords do not match')
return self
```
Model before validator example
Example of a model before validator that checks for presence of a field:
```python
from typing import Any
from pydantic import BaseModel, model_validator
class UserModel(BaseModel):
username: str
@model_validator(mode='before')
@classmethod
def check_card_number_not_present(cls, data: Any) -> Any:
if isinstance(data, dict):
if 'card_number' in data:
raise ValueError("'card_number' should not be included")
return data
```
Model wrap validator example
Example of a model wrap validator that logs failed validation:
```python
import logging
from typing import Any
from typing_extensions import Self
from pydantic import BaseModel, ModelWrapValidatorHandler, ValidationError, model_validator
class UserModel(BaseModel):
username: str
@model_validator(mode='wrap')
@classmethod
def log_failed_validation(cls, data: Any, handler: ModelWrapValidatorHandler[Self]) -> Self:
try:
return handler(data)
except ValidationError:
logging.error('Model %s failed to validate with data %s', cls, data)
raise
```
Model validator must return instance
After model validators are defined as instance methods and must return the validated instance of type Self.
Model before validator handles raw input
Model before validators handle raw input which can be any arbitrary object. Most commonly the input data will be a dictionary when calling Model(field=value), but it could also be an arbitrary class instance if from_attributes configuration is set.
Model before validator mutation pitfall
When using model before validators, avoid mutating the value directly if raising a validation error later in the validator function, as the mutated value may be passed to other validators if using unions.
Model validator inheritance
A model validator defined in a base class will be called during the validation of a subclass instance. Overriding a model validator in a subclass will override the base class validator, and thus only the subclass version will be called.
Validation error exceptions
Three types of exceptions can be raised inside validators: ValueError which is the most common; AssertionError using the assert statement, but note these are skipped when Python runs with the -O optimization flag; PydanticCustomError which is more verbose but provides extra flexibility.
PydanticCustomError example
Example of raising a PydanticCustomError in a field validator:
```python
from pydantic_core import PydanticCustomError
from pydantic import BaseModel, ValidationError, field_validator
class Model(BaseModel):
x: int
@field_validator('x', mode='after')
@classmethod
def validate_x(cls, v: int) -> int:
if v % 42 == 0:
raise PydanticCustomError(
'the_answer_error',
'{number} is the answer!',
{'number': v},
)
return v
try:
Model(x=42 * 2)
except ValidationError as e:
print(e)
```
ValidationInfo provides extra validator information
Both field and model validators callables in all modes can optionally take an extra ValidationInfo argument providing: already validated data via the data property, user defined context via the context property, the current validation mode (either 'python', 'json', or 'strings') via the mode property, and the current field name for field validators via the field_name property.
ValidationInfo data property for field validators
For field validators, the already validated data can be accessed using the data property of ValidationInfo. This provides access to previously validated fields in the order they were defined. Note that you must ensure you are not accessing a field that hasn't been validated yet.
ValidationInfo data example
Example of using ValidationInfo.data to check if password fields match:
```python
from pydantic import BaseModel, ValidationInfo, field_validator
class UserModel(BaseModel):
password: str
password_repeat: str
username: str
@field_validator('password_repeat', mode='after')
@classmethod
def check_passwords_match(cls, value: str, info: ValidationInfo) -> str:
if value != info.data['password']:
raise ValueError('Passwords do not match')
return value
```
ValidationInfo data is None for model validators
The data property of ValidationInfo is None when using model validators, unlike field validators where it contains already validated field data.
ValidationInfo context from model_validate
A context object can be passed to model validation methods like model_validate(), which can be accessed inside validator functions using the context property of ValidationInfo.
ValidationInfo context example
Example of passing context to model_validate and using it in a field validator:
```python
from pydantic import BaseModel, ValidationInfo, field_validator
class Model(BaseModel):
text: str
@field_validator('text', mode='after')
@classmethod
def remove_stopwords(cls, v: str, info: ValidationInfo) -> str:
if isinstance(info.context, dict):
stopwords = info.context.get('stopwords', set())
v = ' '.join(w for w in v.split() if w.lower() not in stopwords)
return v
data = {'text': 'This is an example document'}
print(Model.model_validate(data))
#> text='This is an example document'
print(Model.model_validate(data, context={'stopwords': ['this', 'is', 'an']}))
#> text='example document'
```
Context not available in direct model instantiation
It is currently not possible to provide a context when directly instantiating a model by calling Model(...). A workaround is to use a ContextVar and a custom __init__ method.
Validator ordering in annotated pattern
When using the annotated pattern, the order of validator application is: before and wrap validators run from right to left, then after validators run from left to right.
Decorator validators converted to annotated form
Validators defined using the @field_validator decorator are internally converted to their annotated form counterpart and added last after the existing metadata for the field. The same validator ordering logic applies.
InstanceOf validator usage
InstanceOf can be used to validate that a value is an instance of a given class.
Example:
```python
from pydantic import BaseModel, InstanceOf, ValidationError
class Fruit:
def __repr__(self):
return self.__class__.__name__
class Banana(Fruit): ...
class Apple(Fruit): ...
class Basket(BaseModel):
fruits: list[InstanceOf[Fruit]]
print(Basket(fruits=[Banana(), Apple()]))
#> fruits=[Banana, Apple]
```
SkipValidation utility
SkipValidation can be used to skip validation on a field. When using SkipValidation, validation of that field is not performed.
Example:
```python
from pydantic import BaseModel, SkipValidation
class Model(BaseModel):
names: list[SkipValidation[str]]
m = Model(names=['foo', 'bar'])
print(m)
#> names=['foo', 'bar']
m = Model(names=['foo', 123])
print(m)
#> names=['foo', 123]
```
Note that if a field with SkipValidation has the wrong type, it will emit a warning during serialization.
ValidateAs validator for custom types
ValidateAs can be used to validate a custom type from a type natively supported by Pydantic. This is particularly useful when using custom types with multiple fields.
ValidateAs example
Example of using ValidateAs to validate a custom class from a dictionary:
```python
from typing import Annotated
from pydantic import BaseModel, TypeAdapter, ValidateAs
class MyCls:
def __init__(self, a: int) -> None:
self.a = a
def __repr__(self) -> str:
return f"MyCls(a={self.a})"
class ValModel(BaseModel):
a: int
ta = TypeAdapter(
Annotated[MyCls, ValidateAs(ValModel, lambda v: MyCls(a=v.a))]
)
print(ta.validate_python({'a': 1}))
#> MyCls(a=1)
```
PydanticUseDefault exception for default values
PydanticUseDefault can be used to notify Pydantic that the default value should be used instead of the provided input value.
PydanticUseDefault example
Example of using PydanticUseDefault to use the default value when None is passed:
```python
from typing import Annotated, Any
from pydantic_core import PydanticUseDefault
from pydantic import BaseModel, BeforeValidator
def default_if_none(value: Any) -> Any:
if value is None:
raise PydanticUseDefault()
return value
class Model(BaseModel):
name: Annotated[str, BeforeValidator(default_if_none)] = 'default_name'
print(Model(name=None))
#> name='default_name'
```
Default values not validated by default
Default values of fields are not validated unless configured to do so, and thus custom validators will not be applied to default values by default.
Before/plain/wrap validators and JSON Schema input type
When using before, plain, or wrap field validators, the accepted input type may be different from the field annotation. The json_schema_input_type argument can be provided to specify the correct input type for JSON Schema generation.
json_schema_input_type example
Example of using json_schema_input_type to specify that a string field also accepts integers:
```python
from typing import Any
from pydantic import BaseModel, field_validator
class Model(BaseModel):
value: str
@field_validator('value', mode='before', json_schema_input_type=int | str)
@classmethod
def cast_ints(cls, value: Any) -> Any:
if isinstance(value, int):
return str(value)
else:
return value
print(Model.model_json_schema()['properties']['value'])
#> {'anyOf': [{'type': 'integer'}, {'type': 'string'}], 'title': 'Value'}
```
json_schema_input_type defaults to field type or Any
If json_schema_input_type is not provided, Pydantic will use the field type by default. However, for plain validators, json_schema_input_type defaults to Any since the field type is completely discarded by plain validators.
Use built-in validation constraints instead of custom validators
Use built-in validation constraints from Field() or annotated_types instead of defining custom validators whenever possible. For example, use Annotated[int, Gt(1)] instead of writing a field_validator that checks if value > 1.
Prefer after validators over before validators
Use after validators because they run after Pydantic validation, guaranteeing you work with the correct field type. Before validators are more error-prone because input data can be anything, especially for model validators where input might not be a dict but an arbitrary object.
Annotated pattern for validators over decorator pattern
Prefer using the Annotated pattern with validators like AfterValidator placed next to the field definition, making it easy to understand. The decorator pattern with field_validator can lead to unclear behavior, especially when considering execution order with subclasses.
AfterValidator and field_validator example
Example comparing Annotated validator pattern with decorator pattern:
from pydantic import BaseModel, field_validator, AfterValidator
from typing import Annotated
def is_even(value: int) -> int:
if value % 2 == 1:
raise ValueError(f'{value} is not an even number')
return value
class Model(BaseModel):
even: Annotated[int, AfterValidator(is_even)]
odd: int
@field_validator('odd', mode='after')
@classmethod
def is_odd(cls, value: int) -> int:
if value % 2 == 0:
raise ValueError(f'{value} is not an odd number')
return value
Prefer the Annotated form as it places the validator next to the field.