Строгий режим
??? API "Документация по API" pydantic.types.Strict
По умолчанию Pydantic попытается привести значения к желаемому типу, если это возможно. Например, вы можете передать строку "123"
в качестве входных данных в поле int
, и она будет преобразована в 123
. Такое поведение принуждения полезно во многих сценариях — например: UUID, параметры URL, заголовки HTTP, переменные среды, пользовательский ввод и т. д.
Однако бывают ситуации, когда это нежелательно, и вы хотите, чтобы Pydantic выдавал ошибку, а не принуждал данные.
Чтобы лучше поддерживать этот вариант использования, Pydantic предоставляет «строгий режим», который можно включить для каждой модели, для каждого поля или даже для каждого вызова проверки. Когда включен строгий режим, Pydantic будет гораздо менее снисходителен при принудительном использовании данных и вместо этого выдаст ошибку, если данные не правильного типа.
Вот краткий пример, показывающий разницу между поведением проверки в строгом режиме и режиме по умолчанию/слабое:
from pydantic import BaseModel, ValidationError
class MyModel(BaseModel):
x: int
print(MyModel.model_validate({'x': '123'})) # lax mode
#> x=123
try:
MyModel.model_validate({'x': '123'}, strict=True) # strict mode
except ValidationError as exc:
print(exc)
"""
1 validation error for MyModel
x
Input should be a valid integer [type=int_type, input_value='123', input_type=str]
"""
Существуют различные способы получения проверки строгого режима при использовании Pydantic, которые будут более подробно обсуждаться ниже:
- Передача
strict=True
методам проверки , таким какBaseModel.model_validate
,TypeAdapter.validate_python
и аналогичным для JSON. - Использование
Field(strict=True)
с полямиBaseModel
,dataclass
илиTypedDict
- Использование
pydantic.types.Strict
в качестве аннотации типа поля- Pydantic предоставляет некоторые псевдонимы типов, которые уже помечены
Strict
, напримерpydantic.types.StrictInt
- Pydantic предоставляет некоторые псевдонимы типов, которые уже помечены
- Использование
ConfigDict(strict=True)
Приведение типов в строгом режиме¶
Для большинства типов при проверке данных из Python в строгом режиме принимаются только экземпляры конкретных типов. Например, при проверке поля int
принимаются только экземпляры int
; передача экземпляров float
или str
приведет к возникновению ValidationError
.
Обратите внимание, что мы более свободны при проверке данных из JSON в строгом режиме. Например, при проверке поля UUID
экземпляры str
будут приниматься при проверке из JSON, но не из Python:
import json
from uuid import UUID
from pydantic import BaseModel, ValidationError
class MyModel(BaseModel):
guid: UUID
data = {'guid': '12345678-1234-1234-1234-123456789012'}
print(MyModel.model_validate(data)) # OK: lax
#> guid=UUID('12345678-1234-1234-1234-123456789012')
print(
MyModel.model_validate_json(json.dumps(data), strict=True)
) # OK: strict, but from json
#> guid=UUID('12345678-1234-1234-1234-123456789012')
try:
MyModel.model_validate(data, strict=True) # Not OK: strict, from python
except ValidationError as exc:
print(exc.errors(include_url=False))
"""
[
{
'type': 'is_instance_of',
'loc': ('guid',),
'msg': 'Input should be an instance of UUID',
'input': '12345678-1234-1234-1234-123456789012',
'ctx': {'class': 'UUID'},
}
]
"""
Для получения более подробной информации о том, какие типы разрешены в качестве входных данных в строгом режиме, вы можете просмотреть Таблицу преобразования .
Строгий режим при вызове методов¶
Все включенные до сих пор примеры проходят проверку в строгом режиме за счет использования strict=True
в качестве аргумента ключевого слова для методов проверки. Хотя мы показали это для BaseModel.model_validate
, это также работает с произвольными типами благодаря использованию TypeAdapter
:
from pydantic import TypeAdapter, ValidationError
print(TypeAdapter(bool).validate_python('yes')) # OK: lax
#> True
try:
TypeAdapter(bool).validate_python('yes', strict=True) # Not OK: strict
except ValidationError as exc:
print(exc)
"""
1 validation error for bool
Input should be a valid boolean [type=bool_type, input_value='yes', input_type=str]
"""
Обратите внимание, что это также работает даже при использовании более «сложных» типов в TypeAdapter
:
from dataclasses import dataclass
from pydantic import TypeAdapter, ValidationError
@dataclass
class MyDataclass:
x: int
try:
TypeAdapter(MyDataclass).validate_python({'x': '123'}, strict=True)
except ValidationError as exc:
print(exc)
"""
1 validation error for MyDataclass
Input should be an instance of MyDataclass [type=dataclass_exact_type, input_value={'x': '123'}, input_type=dict]
"""
Это также работает с методами TypeAdapter.validate_json
и BaseModel.model_validate_json
:
import json
from typing import List
from uuid import UUID
from pydantic import BaseModel, TypeAdapter, ValidationError
try:
TypeAdapter(List[int]).validate_json('["1", 2, "3"]', strict=True)
except ValidationError as exc:
print(exc)
"""
2 validation errors for list[int]
0
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
2
Input should be a valid integer [type=int_type, input_value='3', input_type=str]
"""
class Model(BaseModel):
x: int
y: UUID
data = {'x': '1', 'y': '12345678-1234-1234-1234-123456789012'}
try:
Model.model_validate(data, strict=True)
except ValidationError as exc:
# Neither x nor y are valid in strict mode from python:
print(exc)
"""
2 validation errors for Model
x
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
y
Input should be an instance of UUID [type=is_instance_of, input_value='12345678-1234-1234-1234-123456789012', input_type=str]
"""
json_data = json.dumps(data)
try:
Model.model_validate_json(json_data, strict=True)
except ValidationError as exc:
# From JSON, x is still not valid in strict mode, but y is:
print(exc)
"""
1 validation error for Model
x
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
"""
Строгий режим с Field
¶
Для отдельных полей модели вы можете установить strict=True
для поля . Это приведет к использованию строгого режима проверки для этого поля, даже если методы проверки вызываются без strict=True
.
Будут затронуты только поля, для которых установлено strict=True
:
from pydantic import BaseModel, Field, ValidationError
class User(BaseModel):
name: str
age: int
n_pets: int
user = User(name='John', age='42', n_pets='1')
print(user)
#> name='John' age=42 n_pets=1
class AnotherUser(BaseModel):
name: str
age: int = Field(strict=True)
n_pets: int
try:
anotheruser = AnotherUser(name='John', age='42', n_pets='1')
except ValidationError as e:
print(e)
"""
1 validation error for AnotherUser
age
Input should be a valid integer [type=int_type, input_value='42', input_type=str]
"""
Обратите внимание, что строгость полей также повлияет на проверку, выполняемую при создании экземпляра класса модели:
from pydantic import BaseModel, Field, ValidationError
class Model(BaseModel):
x: int = Field(strict=True)
y: int = Field(strict=False)
try:
Model(x='1', y='2')
except ValidationError as exc:
print(exc)
"""
1 validation error for Model
x
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
"""
Использование Field
в качестве аннотации¶
Обратите внимание, что Field(strict=True)
(или с любыми другими ключевыми аргументами) при необходимости можно использовать в качестве аннотации, например, при работе с TypedDict
:
from typing_extensions import Annotated, TypedDict
from pydantic import Field, TypeAdapter, ValidationError
class MyDict(TypedDict):
x: Annotated[int, Field(strict=True)]
try:
TypeAdapter(MyDict).validate_python({'x': '1'})
except ValidationError as exc:
print(exc)
"""
1 validation error for typed-dict
x
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
"""
Строгий режим с Annotated[..., Strict()]
¶
??? API "Документация по API" pydantic.types.Strict
Pydantic также предоставляет класс Strict
, который предназначен для использования в качестве метаданных с классом typing.Annotated
; эта аннотация указывает, что аннотированное поле должно быть проверено в строгом режиме:
from typing_extensions import Annotated
from pydantic import BaseModel, Strict, ValidationError
class User(BaseModel):
name: str
age: int
is_active: Annotated[bool, Strict()]
User(name='David', age=33, is_active=True)
try:
User(name='David', age=33, is_active='True')
except ValidationError as exc:
print(exc)
"""
1 validation error for User
is_active
Input should be a valid boolean [type=bool_type, input_value='True', input_type=str]
"""
Фактически, это метод, используемый для реализации некоторых стандартных типов, предоставляемых Pydantic, таких как StrictInt
.
Строгий режим с ConfigDict
¶
BaseModel
¶
Если вы хотите включить строгий режим для всех полей сложного типа ввода, вы можете использовать ConfigDict(strict=True)
в model_config
:
from pydantic import BaseModel, ConfigDict, ValidationError
class User(BaseModel):
model_config = ConfigDict(strict=True)
name: str
age: int
is_active: bool
try:
User(name='David', age='33', is_active='yes')
except ValidationError as exc:
print(exc)
"""
2 validation errors for User
age
Input should be a valid integer [type=int_type, input_value='33', input_type=str]
is_active
Input should be a valid boolean [type=bool_type, input_value='yes', input_type=str]
"""
!!! note Примечание. При использовании strict=True
через model_config
модели вы все равно можете переопределить строгость отдельных полей, установив strict=False
для отдельных полей:
```py
from pydantic import BaseModel, ConfigDict, Field
class User(BaseModel):
model_config = ConfigDict(strict=True)
name: str
age: int = Field(strict=False)
```
Обратите внимание, что строгий режим не применяется рекурсивно к вложенным полям модели:
from pydantic import BaseModel, ConfigDict, ValidationError
class Inner(BaseModel):
y: int
class Outer(BaseModel):
model_config = ConfigDict(strict=True)
x: int
inner: Inner
print(Outer(x=1, inner=Inner(y='2')))
#> x=1 inner=Inner(y=2)
try:
Outer(x='1', inner=Inner(y='2'))
except ValidationError as exc:
print(exc)
"""
1 validation error for Outer
x
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
"""
(Это также относится к классам данных и TypedDict
.)
Если это нежелательно, следует убедиться, что строгий режим включен для всех задействованных типов. Например, это можно сделать для классов моделей, используя общий базовый класс с model_config = ConfigDict(strict=True)
:
from pydantic import BaseModel, ConfigDict, ValidationError
class MyBaseModel(BaseModel):
model_config = ConfigDict(strict=True)
class Inner(MyBaseModel):
y: int
class Outer(MyBaseModel):
x: int
inner: Inner
try:
Outer.model_validate({'x': 1, 'inner': {'y': '2'}})
except ValidationError as exc:
print(exc)
"""
1 validation error for Outer
inner.y
Input should be a valid integer [type=int_type, input_value='2', input_type=str]
"""
Классы данных и TypedDict
¶
Классы данных Pydantic ведут себя аналогично примерам, показанным выше с BaseModel
, с той лишь разницей, что вместо model_config
вы должны использовать аргумент ключевого слова config
для @pydantic.dataclasses.dataclass
декоратор.
Если это возможно, вы можете добиться вложенного строгого режима для ванильных классов данных или подклассов TypedDict
, аннотируя поля аннотацией pydantic.types.Strict
.
Однако если это невозможно (например, при работе со сторонними типами), вы можете установить конфигурацию, которую Pydantic должен использовать для типа, установив атрибут __pydantic_config__
для типа:
from typing_extensions import TypedDict
from pydantic import ConfigDict, TypeAdapter, ValidationError
class Inner(TypedDict):
y: int
Inner.__pydantic_config__ = ConfigDict(strict=True)
class Outer(TypedDict):
x: int
inner: Inner
adapter = TypeAdapter(Outer)
print(adapter.validate_python({'x': '1', 'inner': {'y': 2}}))
#> {'x': 1, 'inner': {'y': 2}}
try:
adapter.validate_python({'x': '1', 'inner': {'y': '2'}})
except ValidationError as exc:
print(exc)
"""
1 validation error for typed-dict
inner.y
Input should be a valid integer [type=int_type, input_value='2', input_type=str]
"""
TypeAdapter
¶
Вы также можете получить строгий режим, используя аргумент ключевого слова config для класса TypeAdapter
:
from pydantic import ConfigDict, TypeAdapter, ValidationError
adapter = TypeAdapter(bool, config=ConfigDict(strict=True))
try:
adapter.validate_python('yes')
except ValidationError as exc:
print(exc)
"""
1 validation error for bool
Input should be a valid boolean [type=bool_type, input_value='yes', input_type=str]
"""
@validate_call
¶
Строгий режим также можно использовать с декоратором @validate_call
передав аргумент ключевого слова config
:
from pydantic import ConfigDict, ValidationError, validate_call
@validate_call(config=ConfigDict(strict=True))
def foo(x: int) -> int:
return x
try:
foo('1')
except ValidationError as exc:
print(exc)
"""
1 validation error for foo
0
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
"""
本文总阅读量次