Model Config
API Documentation
Behaviour of Pydantic can be controlled via the BaseModel.model_config
,
and as an argument to TypeAdapter
.
Note
Before v2.0, the Config
class was used. This is still supported, but deprecated.
from pydantic import BaseModel, ConfigDict, ValidationError
class Model(BaseModel):
model_config = ConfigDict(str_max_length=10)
v: str
try:
m = Model(v='x' * 20)
except ValidationError as e:
print(e)
"""
1 validation error for Model
v
String should have at most 10 characters [type=string_too_long, input_value='xxxxxxxxxxxxxxxxxxxx', input_type=str]
"""
Also, you can specify config options as model class kwargs:
from pydantic import BaseModel, ValidationError
class Model(BaseModel, extra='forbid'): # (1)!
a: str
try:
Model(a='spam', b='oh no')
except ValidationError as e:
print(e)
"""
1 validation error for Model
b
Extra inputs are not permitted [type=extra_forbidden, input_value='oh no', input_type=str]
"""
- See the Extra Attributes section for more details.
Similarly, if using the @dataclass
decorator from pydantic:
from datetime import datetime
from pydantic import ConfigDict, ValidationError
from pydantic.dataclasses import dataclass
config = ConfigDict(str_max_length=10, validate_assignment=True)
@dataclass(config=config) # (1)!
class User:
id: int
name: str = 'John Doe'
signup_ts: datetime = None
user = User(id='42', signup_ts='2032-06-21T12:00')
try:
user.name = 'x' * 20
except ValidationError as e:
print(e)
"""
1 validation error for User
name
String should have at most 10 characters [type=string_too_long, input_value='xxxxxxxxxxxxxxxxxxxx', input_type=str]
"""
-
If using the
dataclass
from the standard library orTypedDict
, you should use__pydantic_config__
instead. See:from dataclasses import dataclass from datetime import datetime from pydantic import ConfigDict @dataclass class User: __pydantic_config__ = ConfigDict(strict=True) id: int name: str = 'John Doe' signup_ts: datetime = None
Options¶
See the ConfigDict
API documentation for the full list of settings.
Change behaviour globally¶
If you wish to change the behaviour of Pydantic globally, you can create your own custom BaseModel
with custom model_config
since the config is inherited:
from pydantic import BaseModel, ConfigDict
class Parent(BaseModel):
model_config = ConfigDict(extra='allow')
class Model(Parent):
x: str
m = Model(x='foo', y='bar')
print(m.model_dump())
#> {'x': 'foo', 'y': 'bar'}
- Since
Parent
is a subclass ofBaseModel
, it will inherit themodel_config
attribute. This means thatModel
will haveextra='allow'
by default.
If you add a model_config
to the Model
class, it will merge with the model_config
from Parent
:
from pydantic import BaseModel, ConfigDict
class Parent(BaseModel):
model_config = ConfigDict(extra='allow')
class Model(Parent):
model_config = ConfigDict(str_to_lower=True) # (1)!
x: str
m = Model(x='FOO', y='bar')
print(m.model_dump())
#> {'x': 'foo', 'y': 'bar'}
print(m.model_config)
#> {'extra': 'allow', 'str_to_lower': True}
Alias Generator¶
If data source field names do not match your code style (e. g. CamelCase fields),
you can automatically generate aliases using alias_generator
:
from pydantic import BaseModel, ConfigDict
def to_camel(string: str) -> str:
return ''.join(word.capitalize() for word in string.split('_'))
class Voice(BaseModel):
model_config = ConfigDict(alias_generator=to_camel)
name: str
language_code: str
voice = Voice(Name='Filiz', LanguageCode='tr-TR')
print(voice.language_code)
#> tr-TR
print(voice.model_dump(by_alias=True))
#> {'Name': 'Filiz', 'LanguageCode': 'tr-TR'}
Here camel case refers to "upper camel case" aka pascal case
e.g. CamelCase
. If you'd like instead to use lower camel case e.g. camelCase
,
instead use the to_lower_camel
function.
Alias Precedence¶
If you specify an alias
on the Field
, it will take precedence over the generated alias by default:
from pydantic import BaseModel, ConfigDict, Field
def to_camel(string: str) -> str:
return ''.join(word.capitalize() for word in string.split('_'))
class Voice(BaseModel):
model_config = ConfigDict(alias_generator=to_camel)
name: str
language_code: str = Field(alias='lang')
voice = Voice(Name='Filiz', lang='tr-TR')
print(voice.language_code)
#> tr-TR
print(voice.model_dump(by_alias=True))
#> {'Name': 'Filiz', 'lang': 'tr-TR'}
Alias Priority¶
You may set alias_priority
on a field to change this behavior:
alias_priority=2
the alias will not be overridden by the alias generator.alias_priority=1
the alias will be overridden by the alias generator.alias_priority
not set, the alias will be overridden by the alias generator.
The same precedence applies to validation_alias
and serialization_alias
.
See more about the different field aliases under field aliases.
Extra Attributes¶
You can configure how pydantic handles the attributes that are not defined in the model:
allow
- Allow any extra attributes.forbid
- Forbid any extra attributes.ignore
- Ignore any extra attributes.
The default value is 'ignore'
.
from pydantic import BaseModel, ConfigDict
class User(BaseModel):
model_config = ConfigDict(extra='ignore') # (1)!
name: str
user = User(name='John Doe', age=20) # (2)!
print(user)
#> name='John Doe'
- This is the the default behaviour.
- The
age
argument is ignored.
Instead, with extra='allow'
, the age
argument is included:
from pydantic import BaseModel, ConfigDict
class User(BaseModel):
model_config = ConfigDict(extra='allow')
name: str
user = User(name='John Doe', age=20) # (1)!
print(user)
#> name='John Doe' age=20
- The
age
argument is included.
With extra='forbid'
, an error is raised:
from pydantic import BaseModel, ConfigDict, ValidationError
class User(BaseModel):
model_config = ConfigDict(extra='forbid')
name: str
try:
User(name='John Doe', age=20)
except ValidationError as e:
print(e)
"""
1 validation error for User
age
Extra inputs are not permitted [type=extra_forbidden, input_value=20, input_type=int]
"""
Populate by Name¶
In case you set an alias, you can still populate the model by the original name.
You need to set populate_by_name=True
in the model_config
:
from pydantic import BaseModel, ConfigDict, Field
class User(BaseModel):
model_config = ConfigDict(populate_by_name=True)
name: str = Field(alias='full_name') # (1)!
age: int
user = User(full_name='John Doe', age=20) # (2)!
print(user)
#> name='John Doe' age=20
user = User(name='John Doe', age=20) # (3)!
print(user)
#> name='John Doe' age=20
- The field
'name'
has an alias'full_name'
. - The model is populated by the alias
'full_name'
. - The model is populated by the field name
'name'
.
Validate Assignment¶
The default behavior of Pydantic is to validate the data when the model is created.
In case the user changes the data after the model is created, the model is not revalidated.
from pydantic import BaseModel
class User(BaseModel):
name: str
user = User(name='John Doe') # (1)!
print(user)
#> name='John Doe'
user.name = 123 # (1)!
print(user)
#> name=123
- The validation happens only when the model is created.
- The validation does not happen when the data is changed.
In case you want to revalidate the model when the data is changed, you can use validate_assignment=True
:
from pydantic import BaseModel, ValidationError
class User(BaseModel, validate_assignment=True): # (1)!
name: str
user = User(name='John Doe') # (2)!
print(user)
#> name='John Doe'
try:
user.name = 123 # (3)!
except ValidationError as e:
print(e)
"""
1 validation error for User
name
Input should be a valid string [type=string_type, input_value=123, input_type=int]
"""
- You can either use class keyword arguments, or
model_config
to setvalidate_assignment=True
. - The validation happens when the model is created.
- The validation also happens when the data is changed.
Revalidate instances¶
By default, model and dataclass instances are not revalidated during validation.
from typing import List
from pydantic import BaseModel
class User(BaseModel, revalidate_instances='never'): # (1)!
hobbies: List[str]
class SubUser(User):
sins: List[str]
class Transaction(BaseModel):
user: User
my_user = User(hobbies=['reading'])
t = Transaction(user=my_user)
print(t)
#> user=User(hobbies=['reading'])
my_user.hobbies = [1] # (2)!
t = Transaction(user=my_user) # (3)!
print(t)
#> user=User(hobbies=[1])
my_sub_user = SubUser(hobbies=['scuba diving'], sins=['lying'])
t = Transaction(user=my_sub_user)
print(t)
#> user=SubUser(hobbies=['scuba diving'], sins=['lying'])
revalidate_instances
is set to'never'
by **default.- The assignment is not validated, unless you set
validate_assignment
toTrue
in the model's config. - Since
revalidate_instances
is set tonever
, this is not revalidated.
If you want to revalidate instances during validation, you can set revalidate_instances
to 'always'
in the model's config.
from typing import List
from pydantic import BaseModel, ValidationError
class User(BaseModel, revalidate_instances='always'): # (1)!
hobbies: List[str]
class SubUser(User):
sins: List[str]
class Transaction(BaseModel):
user: User
my_user = User(hobbies=['reading'])
t = Transaction(user=my_user)
print(t)
#> user=User(hobbies=['reading'])
my_user.hobbies = [1]
try:
t = Transaction(user=my_user) # (2)!
except ValidationError as e:
print(e)
"""
1 validation error for Transaction
user.hobbies.0
Input should be a valid string [type=string_type, input_value=1, input_type=int]
"""
my_sub_user = SubUser(hobbies=['scuba diving'], sins=['lying'])
t = Transaction(user=my_sub_user)
print(t) # (3)!
#> user=User(hobbies=['scuba diving'])
revalidate_instances
is set to'always'
.- The model is revalidated, since
revalidate_instances
is set to'always'
. - Using
'never'
we would have gottenuser=SubUser(hobbies=['scuba diving'], sins=['lying'])
.
It's also possible to set revalidate_instances
to 'subclass-instances'
to only revalidate instances
of subclasses of the model.
from typing import List
from pydantic import BaseModel
class User(BaseModel, revalidate_instances='subclass-instances'): # (1)!
hobbies: List[str]
class SubUser(User):
sins: List[str]
class Transaction(BaseModel):
user: User
my_user = User(hobbies=['reading'])
t = Transaction(user=my_user)
print(t)
#> user=User(hobbies=['reading'])
my_user.hobbies = [1]
t = Transaction(user=my_user) # (2)!
print(t)
#> user=User(hobbies=[1])
my_sub_user = SubUser(hobbies=['scuba diving'], sins=['lying'])
t = Transaction(user=my_sub_user)
print(t) # (3)!
#> user=User(hobbies=['scuba diving'])
revalidate_instances
is set to'subclass-instances'
.- This is not revalidated, since
my_user
is not a subclass ofUser
. - Using
'never'
we would have gottenuser=SubUser(hobbies=['scuba diving'], sins=['lying'])
.
Strict Mode¶
By default, Pydantic attempts to coerce values to the correct type, when possible.
There are situations in which you may want to disable this behavior, and instead raise an error if a value's type does not match the field's type annotation.
To configure strict mode for all fields on a model, you can
set model_config = ConfigDict(strict=True)
on the model.
from pydantic import BaseModel, ConfigDict
class Model(BaseModel):
model_config = ConfigDict(strict=True)
name: str
age: int
See Strict Mode for more details.
See the Conversion Table for more details on how Pydantic converts data in both strict and lax modes.
Arbitrary Types Allowed¶
You can allow arbitrary types using the arbitrary_types_allowed
setting in the model's config:
from pydantic import BaseModel, ConfigDict, ValidationError
# This is not a pydantic model, it's an arbitrary class
class Pet:
def __init__(self, name: str):
self.name = name
class Model(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True)
pet: Pet
owner: str
pet = Pet(name='Hedwig')
# A simple check of instance type is used to validate the data
model = Model(owner='Harry', pet=pet)
print(model)
#> pet=<__main__.Pet object at 0x0123456789ab> owner='Harry'
print(model.pet)
#> <__main__.Pet object at 0x0123456789ab>
print(model.pet.name)
#> Hedwig
print(type(model.pet))
#> <class '__main__.Pet'>
try:
# If the value is not an instance of the type, it's invalid
Model(owner='Harry', pet='Hedwig')
except ValidationError as e:
print(e)
"""
1 validation error for Model
pet
Input should be an instance of Pet [type=is_instance_of, input_value='Hedwig', input_type=str]
"""
# Nothing in the instance of the arbitrary type is checked
# Here name probably should have been a str, but it's not validated
pet2 = Pet(name=42)
model2 = Model(owner='Harry', pet=pet2)
print(model2)
#> pet=<__main__.Pet object at 0x0123456789ab> owner='Harry'
print(model2.pet)
#> <__main__.Pet object at 0x0123456789ab>
print(model2.pet.name)
#> 42
print(type(model2.pet))
#> <class '__main__.Pet'>
Protected Namespaces¶
Pydantic prevents collisions between model attributes and BaseModel
's own methods by
namespacing them with the prefix model_
.
import warnings
from pydantic import BaseModel
warnings.filterwarnings('error') # Raise warnings as errors
try:
class Model(BaseModel):
model_prefixed_field: str
except UserWarning as e:
print(e)
"""
Field "model_prefixed_field" has conflict with protected namespace "model_".
You may be able to resolve this warning by setting `model_config['protected_namespaces'] = ()`.
"""
You can customize this behavior using the protected_namespaces
setting:
import warnings
from pydantic import BaseModel, ConfigDict
warnings.filterwarnings('error') # Raise warnings as errors
try:
class Model(BaseModel):
model_prefixed_field: str
also_protect_field: str
model_config = ConfigDict(
protected_namespaces=('protect_me_', 'also_protect_')
)
except UserWarning as e:
print(e)
"""
Field "also_protect_field" has conflict with protected namespace "also_protect_".
You may be able to resolve this warning by setting `model_config['protected_namespaces'] = ('protect_me_',)`.
"""
While Pydantic will only emit a warning when an item is in a protected namespace but does not actually have a collision, an error is raised if there is an actual collision with an existing attribute:
from pydantic import BaseModel
try:
class Model(BaseModel):
model_validate: str
except NameError as e:
print(e)
"""
Field "model_validate" conflicts with member <bound method BaseModel.model_validate of <class 'pydantic.main.BaseModel'>> of protected namespace "model_".
"""
Hide Input in Errors¶
Pydantic shows the input value and type when it raises ValidationError
during the validation.
from pydantic import BaseModel, ValidationError
class Model(BaseModel):
a: str
try:
Model(a=123)
except ValidationError as e:
print(e)
"""
1 validation error for Model
a
Input should be a valid string [type=string_type, input_value=123, input_type=int]
"""
You can hide the input value and type by setting the hide_input_in_errors
config to True
.
from pydantic import BaseModel, ConfigDict, ValidationError
class Model(BaseModel):
a: str
model_config = ConfigDict(hide_input_in_errors=True)
try:
Model(a=123)
except ValidationError as e:
print(e)
"""
1 validation error for Model
a
Input should be a valid string [type=string_type]
"""