Типы
Там, где это возможно, Pydantic использует стандартные библиотечные типы для определения полей, тем самым сглаживая кривую обучения. Однако для многих полезных приложений не существует стандартного типа библиотеки, поэтому Pydantic реализует многие часто используемые типы.
Существуют также более сложные типы, которые можно найти в пакете дополнительных типов Pydantic .
Если ни один из существующих типов не соответствует вашим целям, вы также можете реализовать свои собственные типы, совместимые с Pydantic, с настраиваемыми свойствами и проверкой.
В следующих разделах описаны типы, поддерживаемые Pydantic.
- Типы стандартной библиотеки — типы из стандартной библиотеки Python.
- Строгие типы — типы, которые позволяют предотвратить приведение со стороны совместимых типов.
- Пользовательские типы данных — создавайте свои собственные типы данных.
- Преобразования типов полей — строгое и нестрогое преобразование между различными типами полей.
Преобразование типов¶
Во время проверки Pydantic может привести данные к ожидаемым типам.
Существует два способа принуждения: строгий и нестрогий. См. Таблицу преобразования для получения более подробной информации о том, как Pydantic преобразует данные как в строгом, так и в нестрогом режимах.
Подробные сведения о включении строгого приведения см. в разделах «Строгий режим» и «Строгие типы» .
Строгие типы¶
Pydantic предоставляет следующие строгие типы:
Эти типы пройдут проверку только в том случае, если проверенное значение относится к соответствующему типу или является подтипом этого типа.
Ограниченные типы¶
Это поведение также проявляется через strict
поле ограниченных типов и может сочетаться с множеством сложных правил проверки. См. отдельные сигнатуры типов поддерживаемых аргументов.
Применяются следующие предостережения:
StrictBytes
(иstrict
опцияconbytes()
) будут принимать как типыbytes
, так иbytearray
.StrictInt
(иstrict
параметрconint()
) не принимают типыbool
, хотяbool
является подклассомint
в Python. Другие подклассы будут работать.StrictFloat
(иstrict
вариантconfloat()
) не принимаютint
.
Помимо вышесказанного, вы также можете иметь тип FiniteFloat
, который будет принимать только конечные значения (т.е. не inf
, -inf
или nan
).
Пользовательские типы¶
Вы также можете определить свои собственные типы данных. Есть несколько способов добиться этого.
Составление типов через Annotated
¶
В PEP 593 Annotated
был представлен как способ прикрепления метаданных времени выполнения к типам без изменения того, как их интерпретируют средства проверки типов. Pydantic использует это преимущество, чтобы позволить вам создавать типы, идентичные исходному типу с точки зрения средств проверки типов, но с добавлением проверки, другой сериализацией и т. д.
Например, чтобы создать тип, представляющий положительное целое число:
# or `from typing import Annotated` for Python 3.9+
from typing_extensions import Annotated
from pydantic import Field, TypeAdapter, ValidationError
PositiveInt = Annotated[int, Field(gt=0)]
ta = TypeAdapter(PositiveInt)
print(ta.validate_python(1))
#> 1
try:
ta.validate_python(-1)
except ValidationError as exc:
print(exc)
"""
1 validation error for constrained-int
Input should be greater than 0 [type=greater_than, input_value=-1, input_type=int]
"""
Обратите внимание, что вы также можете использовать ограничения из аннотированных типов, чтобы сделать этот Pydantic-агностиком:
from annotated_types import Gt
from typing_extensions import Annotated
from pydantic import TypeAdapter, ValidationError
PositiveInt = Annotated[int, Gt(0)]
ta = TypeAdapter(PositiveInt)
print(ta.validate_python(1))
#> 1
try:
ta.validate_python(-1)
except ValidationError as exc:
print(exc)
"""
1 validation error for constrained-int
Input should be greater than 0 [type=greater_than, input_value=-1, input_type=int]
"""
Добавление проверки и сериализации¶
Вы можете добавить или переопределить схемы проверки, сериализации и JSON для произвольного типа, используя маркеры, экспортируемые Pydantic:
from typing_extensions import Annotated
from pydantic import (
AfterValidator,
PlainSerializer,
TypeAdapter,
WithJsonSchema,
)
TruncatedFloat = Annotated[
float,
AfterValidator(lambda x: round(x, 1)),
PlainSerializer(lambda x: f'{x:.1e}', return_type=str),
WithJsonSchema({'type': 'string'}, mode='serialization'),
]
ta = TypeAdapter(TruncatedFloat)
input = 1.02345
assert input != 1.0
assert ta.validate_python(input) == 1.0
assert ta.dump_json(input) == b'"1.0e+00"'
assert ta.json_schema(mode='validation') == {'type': 'number'}
assert ta.json_schema(mode='serialization') == {'type': 'string'}
Дженерики¶
Вы можете использовать переменные типа в Annotated
чтобы вносить в типы многоразовые изменения:
from typing import Any, List, Sequence, TypeVar
from annotated_types import Gt, Len
from typing_extensions import Annotated
from pydantic import ValidationError
from pydantic.type_adapter import TypeAdapter
SequenceType = TypeVar('SequenceType', bound=Sequence[Any])
ShortSequence = Annotated[SequenceType, Len(max_length=10)]
ta = TypeAdapter(ShortSequence[List[int]])
v = ta.validate_python([1, 2, 3, 4, 5])
assert v == [1, 2, 3, 4, 5]
try:
ta.validate_python([1] * 100)
except ValidationError as exc:
print(exc)
"""
1 validation error for list[int]
List should have at most 10 items after validation, not 100 [type=too_long, input_value=[1, 1, 1, 1, 1, 1, 1, 1, ... 1, 1, 1, 1, 1, 1, 1, 1], input_type=list]
"""
T = TypeVar('T') # or a bound=SupportGt
PositiveList = List[Annotated[T, Gt(0)]]
ta = TypeAdapter(PositiveList[float])
v = ta.validate_python([1])
assert type(v[0]) is float
try:
ta.validate_python([-1])
except ValidationError as exc:
print(exc)
"""
1 validation error for list[constrained-float]
0
Input should be greater than 0 [type=greater_than, input_value=-1, input_type=int]
"""
Псевдонимы именованных типов¶
В приведенных выше примерах используются неявные псевдонимы типов. Это означает, что они не смогут иметь title
в схемах JSON, и их схема будет копироваться между полями. Вы можете использовать TypeAliasType
PEP 695 через его бэкпорт расширений ввода для создания именованных псевдонимов, что позволяет вам определять новый тип без создания подклассов. Этот новый тип может быть таким же простым, как имя, или иметь сложную логику проверки:
from typing import List
from annotated_types import Gt
from typing_extensions import Annotated, TypeAliasType
from pydantic import BaseModel
ImplicitAliasPositiveIntList = List[Annotated[int, Gt(0)]]
class Model1(BaseModel):
x: ImplicitAliasPositiveIntList
y: ImplicitAliasPositiveIntList
print(Model1.model_json_schema())
"""
{
'properties': {
'x': {
'items': {'exclusiveMinimum': 0, 'type': 'integer'},
'title': 'X',
'type': 'array',
},
'y': {
'items': {'exclusiveMinimum': 0, 'type': 'integer'},
'title': 'Y',
'type': 'array',
},
},
'required': ['x', 'y'],
'title': 'Model1',
'type': 'object',
}
"""
PositiveIntList = TypeAliasType('PositiveIntList', List[Annotated[int, Gt(0)]])
class Model2(BaseModel):
x: PositiveIntList
y: PositiveIntList
print(Model2.model_json_schema())
"""
{
'$defs': {
'PositiveIntList': {
'items': {'exclusiveMinimum': 0, 'type': 'integer'},
'type': 'array',
}
},
'properties': {
'x': {'$ref': '#/$defs/PositiveIntList'},
'y': {'$ref': '#/$defs/PositiveIntList'},
},
'required': ['x', 'y'],
'title': 'Model2',
'type': 'object',
}
"""
Эти псевдонимы именованных типов также могут быть универсальными:
from typing import Generic, List, TypeVar
from annotated_types import Gt
from typing_extensions import Annotated, TypeAliasType
from pydantic import BaseModel, ValidationError
T = TypeVar('T') # or a `bound=SupportGt`
PositiveList = TypeAliasType(
'PositiveList', List[Annotated[T, Gt(0)]], type_params=(T,)
)
class Model(BaseModel, Generic[T]):
x: PositiveList[T]
assert Model[int].model_validate_json('{"x": ["1"]}').x == [1]
try:
Model[int](x=[-1])
except ValidationError as exc:
print(exc)
"""
1 validation error for Model[int]
x.0
Input should be greater than 0 [type=greater_than, input_value=-1, input_type=int]
"""
Именованные рекурсивные типы¶
Вы также можете использовать TypeAliasType
для создания рекурсивных типов:
from typing import Any, Dict, List, Union
from pydantic_core import PydanticCustomError
from typing_extensions import Annotated, TypeAliasType
from pydantic import (
TypeAdapter,
ValidationError,
ValidationInfo,
ValidatorFunctionWrapHandler,
WrapValidator,
)
def json_custom_error_validator(
value: Any, handler: ValidatorFunctionWrapHandler, _info: ValidationInfo
) -> Any:
"""Simplify the error message to avoid a gross error stemming
from exhaustive checking of all union options.
"""
try:
return handler(value)
except ValidationError:
raise PydanticCustomError(
'invalid_json',
'Input is not valid json',
)
Json = TypeAliasType(
'Json',
Annotated[
Union[Dict[str, 'Json'], List['Json'], str, int, float, bool, None],
WrapValidator(json_custom_error_validator),
],
)
ta = TypeAdapter(Json)
v = ta.validate_python({'x': [1], 'y': {'z': True}})
assert v == {'x': [1], 'y': {'z': True}}
try:
ta.validate_python({'x': object()})
except ValidationError as exc:
print(exc)
"""
1 validation error for function-wrap[json_custom_error_validator()]
Input is not valid json [type=invalid_json, input_value={'x': <object object at 0x0123456789ab>}, input_type=dict]
"""
Настройка проверки с помощью __get_pydantic_core_schema__
¶
Чтобы выполнить более обширную настройку того, как Pydantic обрабатывает пользовательские классы, и, в частности, когда у вас есть доступ к классу или вы можете создать его подкласс, вы можете реализовать специальный __get_pydantic_core_schema__
чтобы сообщить Pydantic, как генерировать схему pydantic-core
.
Хотя pydantic
использует внутреннее pydantic-core
для проверки и сериализации, это новый API для Pydantic V2, поэтому это одна из областей, которая, скорее всего, будет изменена в будущем, и вам следует стараться придерживаться встроенных конструкций, таких как те, которые предоставляются annotated-types
, pydantic.Field
или BeforeValidator
и т. д.
Вы можете реализовать __get_pydantic_core_schema__
как для пользовательского типа, так и для метаданных, предназначенных для размещения в Annotated
. В обоих случаях API похож на промежуточное программное обеспечение и аналогичен API валидаторов «обертывания»: вы получаете source_type
(который не обязательно совпадает с классом, в частности для дженериков) и handler
, который вы можете вызвать с типом либо вызвать следующие метаданные в Annotated
, либо вызвать генерацию внутренней схемы Pydantic.
Самая простая реализация без операций вызывает обработчик заданного типа, а затем возвращает его в качестве результата. Вы также можете изменить тип перед вызовом обработчика, изменить базовую схему, возвращаемую обработчиком, или вообще не вызывать обработчик.
Как метод пользовательского типа¶
Ниже приведен пример типа, который использует __get_pydantic_core_schema__
для настройки способа проверки. Это эквивалентно реализации __get_validators__
в Pydantic V1.
from typing import Any
from pydantic_core import CoreSchema, core_schema
from pydantic import GetCoreSchemaHandler, TypeAdapter
class Username(str):
@classmethod
def __get_pydantic_core_schema__(
cls, source_type: Any, handler: GetCoreSchemaHandler
) -> CoreSchema:
return core_schema.no_info_after_validator_function(cls, handler(str))
ta = TypeAdapter(Username)
res = ta.validate_python('abc')
assert isinstance(res, Username)
assert res == 'abc'
Дополнительные сведения о настройке схем JSON для пользовательских типов см. в разделе «Схема JSON» .
В качестве аннотации¶
Часто вам потребуется параметризовать свой собственный тип не только с помощью параметров универсального типа (что можно сделать через систему типов и будет обсуждаться позже). Или вам может быть все равно (или не хочется) создавать экземпляр вашего подкласса; на самом деле вам нужен исходный тип, просто с дополнительной проверкой.
Например, если бы вы реализовали pydantic.AfterValidator
(см. Добавление проверки и сериализации ) самостоятельно, вы бы сделали что-то похожее на следующее:
from dataclasses import dataclass
from typing import Any, Callable
from pydantic_core import CoreSchema, core_schema
from typing_extensions import Annotated
from pydantic import BaseModel, GetCoreSchemaHandler
@dataclass(frozen=True) # (1)!
class MyAfterValidator:
func: Callable[[Any], Any]
def __get_pydantic_core_schema__(
self, source_type: Any, handler: GetCoreSchemaHandler
) -> CoreSchema:
return core_schema.no_info_after_validator_function(
self.func, handler(source_type)
)
Username = Annotated[str, MyAfterValidator(str.lower)]
class Model(BaseModel):
name: Username
assert Model(name='ABC').name == 'abc' # (2)!
- Спецификация
frozen=True
делаетMyAfterValidator
хешируемым. Без этого невозможен такой союз, какUsername | None
вызовет ошибку. - Обратите внимание, что средства проверки типов не будут жаловаться на присвоение
'ABC'
Username
, как это было в предыдущем примере, поскольку они не считаютUsername
отдельным типом отstr
.
Обработка сторонних типов¶
Другой вариант использования шаблона из предыдущего раздела — обработка сторонних типов.
from typing import Any
from pydantic_core import core_schema
from typing_extensions import Annotated
from pydantic import (
BaseModel,
GetCoreSchemaHandler,
GetJsonSchemaHandler,
ValidationError,
)
from pydantic.json_schema import JsonSchemaValue
class ThirdPartyType:
"""
This is meant to represent a type from a third-party library that wasn't designed with Pydantic
integration in mind, and so doesn't have a `pydantic_core.CoreSchema` or anything.
"""
x: int
def __init__(self):
self.x = 0
class _ThirdPartyTypePydanticAnnotation:
@classmethod
def __get_pydantic_core_schema__(
cls,
_source_type: Any,
_handler: GetCoreSchemaHandler,
) -> core_schema.CoreSchema:
"""
We return a pydantic_core.CoreSchema that behaves in the following ways:
* ints will be parsed as `ThirdPartyType` instances with the int as the x attribute
* `ThirdPartyType` instances will be parsed as `ThirdPartyType` instances without any changes
* Nothing else will pass validation
* Serialization will always return just an int
"""
def validate_from_int(value: int) -> ThirdPartyType:
result = ThirdPartyType()
result.x = value
return result
from_int_schema = core_schema.chain_schema(
[
core_schema.int_schema(),
core_schema.no_info_plain_validator_function(validate_from_int),
]
)
return core_schema.json_or_python_schema(
json_schema=from_int_schema,
python_schema=core_schema.union_schema(
[
# check if it's an instance first before doing any further work
core_schema.is_instance_schema(ThirdPartyType),
from_int_schema,
]
),
serialization=core_schema.plain_serializer_function_ser_schema(
lambda instance: instance.x
),
)
@classmethod
def __get_pydantic_json_schema__(
cls, _core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
) -> JsonSchemaValue:
# Use the same schema that would be used for `int`
return handler(core_schema.int_schema())
# We now create an `Annotated` wrapper that we'll use as the annotation for fields on `BaseModel`s, etc.
PydanticThirdPartyType = Annotated[
ThirdPartyType, _ThirdPartyTypePydanticAnnotation
]
# Create a model class that uses this annotation as a field
class Model(BaseModel):
third_party_type: PydanticThirdPartyType
# Demonstrate that this field is handled correctly, that ints are parsed into `ThirdPartyType`, and that
# these instances are also "dumped" directly into ints as expected.
m_int = Model(third_party_type=1)
assert isinstance(m_int.third_party_type, ThirdPartyType)
assert m_int.third_party_type.x == 1
assert m_int.model_dump() == {'third_party_type': 1}
# Do the same thing where an instance of ThirdPartyType is passed in
instance = ThirdPartyType()
assert instance.x == 0
instance.x = 10
m_instance = Model(third_party_type=instance)
assert isinstance(m_instance.third_party_type, ThirdPartyType)
assert m_instance.third_party_type.x == 10
assert m_instance.model_dump() == {'third_party_type': 10}
# Demonstrate that validation errors are raised as expected for invalid inputs
try:
Model(third_party_type='a')
except ValidationError as e:
print(e)
"""
2 validation errors for Model
third_party_type.is-instance[ThirdPartyType]
Input should be an instance of ThirdPartyType [type=is_instance_of, input_value='a', input_type=str]
third_party_type.chain[int,function-plain[validate_from_int()]]
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
"""
assert Model.model_json_schema() == {
'properties': {
'third_party_type': {'title': 'Third Party Type', 'type': 'integer'}
},
'required': ['third_party_type'],
'title': 'Model',
'type': 'object',
}
Вы можете использовать этот подход, например, для определения поведения для типов Pandas или Numpy.
Использование GetPydanticSchema
для сокращения шаблонного кода¶
??? API "Документация по API" pydantic.types.GetPydanticSchema
Вы можете заметить, что приведенные выше примеры, в которых мы создаем класс маркера, требуют большого количества шаблонов. Во многих простых случаях вы можете значительно минимизировать это, используя pydantic.GetPydanticSchema
:
from pydantic_core import core_schema
from typing_extensions import Annotated
from pydantic import BaseModel, GetPydanticSchema
class Model(BaseModel):
y: Annotated[
str,
GetPydanticSchema(
lambda tp, handler: core_schema.no_info_after_validator_function(
lambda x: x * 2, handler(tp)
)
),
]
assert Model(y='ab').y == 'abab'
Краткое содержание¶
Давайте подведем итоги:
- Pydantic предоставляет перехватчики высокого уровня для настройки типов с помощью
Annotated
таких какAfterValidator
иField
. Используйте их, когда это возможно. - Под капотом они используют
pydantic-core
для настройки проверки, и вы можете подключиться к этому напрямую с помощьюGetPydanticSchema
или класса маркера с__get_pydantic_core_schema__
. - Если вам действительно нужен собственный тип, вы можете реализовать
__get_pydantic_core_schema__
для самого типа.
Обработка пользовательских универсальных классов¶
!!! предупреждение. Это продвинутый метод, который вначале может вам не понадобиться. В большинстве случаев вас, вероятно, устроят стандартные модели Pydantic.
Вы можете использовать универсальные классы в качестве типов полей и выполнять пользовательскую проверку на основе «параметров типа» (или подтипов) с помощью __get_pydantic_core_schema__
.
Если универсальный класс, который вы используете в качестве подтипа, имеет метод класса __get_pydantic_core_schema__
, вам не нужно использовать [arbitrary_types_allowed
][pydantic.config.ConfigDict.dictary_types_allowed], чтобы он работал.
Поскольку параметр source_type
отличается от параметра cls
, вы можете использовать typing.get_args
(или typing_extensions.get_args
) для извлечения общих параметров. Затем вы можете использовать handler
для создания для них схемы, вызвав handler.generate_schema
. Обратите внимание, что мы не делаем что-то вроде handler(get_args(source_type)[0])
потому что мы хотим создать несвязанную схему для этого общего параметра, а не ту, на которую влияет текущий контекст Annotated
метаданных и тому подобное. Это менее важно для пользовательских типов, но имеет решающее значение для аннотированных метаданных, которые изменяют построение схемы.
from dataclasses import dataclass
from typing import Any, Generic, TypeVar
from pydantic_core import CoreSchema, core_schema
from typing_extensions import get_args, get_origin
from pydantic import (
BaseModel,
GetCoreSchemaHandler,
ValidationError,
ValidatorFunctionWrapHandler,
)
ItemType = TypeVar('ItemType')
# This is not a pydantic model, it's an arbitrary generic class
@dataclass
class Owner(Generic[ItemType]):
name: str
item: ItemType
@classmethod
def __get_pydantic_core_schema__(
cls, source_type: Any, handler: GetCoreSchemaHandler
) -> CoreSchema:
origin = get_origin(source_type)
if origin is None: # used as `x: Owner` without params
origin = source_type
item_tp = Any
else:
item_tp = get_args(source_type)[0]
# both calling handler(...) and handler.generate_schema(...)
# would work, but prefer the latter for conceptual and consistency reasons
item_schema = handler.generate_schema(item_tp)
def val_item(
v: Owner[Any], handler: ValidatorFunctionWrapHandler
) -> Owner[Any]:
v.item = handler(v.item)
return v
python_schema = core_schema.chain_schema(
# `chain_schema` means do the following steps in order:
[
# Ensure the value is an instance of Owner
core_schema.is_instance_schema(cls),
# Use the item_schema to validate `items`
core_schema.no_info_wrap_validator_function(
val_item, item_schema
),
]
)
return core_schema.json_or_python_schema(
# for JSON accept an object with name and item keys
json_schema=core_schema.chain_schema(
[
core_schema.typed_dict_schema(
{
'name': core_schema.typed_dict_field(
core_schema.str_schema()
),
'item': core_schema.typed_dict_field(item_schema),
}
),
# after validating the json data convert it to python
core_schema.no_info_before_validator_function(
lambda data: Owner(
name=data['name'], item=data['item']
),
# note that we re-use the same schema here as below
python_schema,
),
]
),
python_schema=python_schema,
)
class Car(BaseModel):
color: str
class House(BaseModel):
rooms: int
class Model(BaseModel):
car_owner: Owner[Car]
home_owner: Owner[House]
model = Model(
car_owner=Owner(name='John', item=Car(color='black')),
home_owner=Owner(name='James', item=House(rooms=3)),
)
print(model)
"""
car_owner=Owner(name='John', item=Car(color='black')) home_owner=Owner(name='James', item=House(rooms=3))
"""
try:
# If the values of the sub-types are invalid, we get an error
Model(
car_owner=Owner(name='John', item=House(rooms=3)),
home_owner=Owner(name='James', item=Car(color='black')),
)
except ValidationError as e:
print(e)
"""
2 validation errors for Model
wine
Input should be a valid number, unable to parse string as a number [type=float_parsing, input_value='Kinda good', input_type=str]
cheese
Input should be a valid boolean, unable to interpret input [type=bool_parsing, input_value='yeah', input_type=str]
"""
# Similarly with JSON
model = Model.model_validate_json(
'{"car_owner":{"name":"John","item":{"color":"black"}},"home_owner":{"name":"James","item":{"rooms":3}}}'
)
print(model)
"""
car_owner=Owner(name='John', item=Car(color='black')) home_owner=Owner(name='James', item=House(rooms=3))
"""
try:
Model.model_validate_json(
'{"car_owner":{"name":"John","item":{"rooms":3}},"home_owner":{"name":"James","item":{"color":"black"}}}'
)
except ValidationError as e:
print(e)
"""
2 validation errors for Model
car_owner.item.color
Field required [type=missing, input_value={'rooms': 3}, input_type=dict]
home_owner.item.rooms
Field required [type=missing, input_value={'color': 'black'}, input_type=dict]
"""
Общие контейнеры¶
Ту же идею можно применить для создания универсальных типов контейнеров, например пользовательского типа Sequence
:
from typing import Any, Sequence, TypeVar
from pydantic_core import ValidationError, core_schema
from typing_extensions import get_args
from pydantic import BaseModel, GetCoreSchemaHandler
T = TypeVar('T')
class MySequence(Sequence[T]):
def __init__(self, v: Sequence[T]):
self.v = v
def __getitem__(self, i):
return self.v[i]
def __len__(self):
return len(self.v)
@classmethod
def __get_pydantic_core_schema__(
cls, source: Any, handler: GetCoreSchemaHandler
) -> core_schema.CoreSchema:
instance_schema = core_schema.is_instance_schema(cls)
args = get_args(source)
if args:
# replace the type and rely on Pydantic to generate the right schema
# for `Sequence`
sequence_t_schema = handler.generate_schema(Sequence[args[0]])
else:
sequence_t_schema = handler.generate_schema(Sequence)
non_instance_schema = core_schema.no_info_after_validator_function(
MySequence, sequence_t_schema
)
return core_schema.union_schema([instance_schema, non_instance_schema])
class M(BaseModel):
model_config = dict(validate_default=True)
s1: MySequence = [3]
m = M()
print(m)
#> s1=<__main__.MySequence object at 0x0123456789ab>
print(m.s1.v)
#> [3]
class M(BaseModel):
s1: MySequence[int]
M(s1=[1])
try:
M(s1=['a'])
except ValidationError as exc:
print(exc)
"""
2 validation errors for M
s1.is-instance[MySequence]
Input should be an instance of MySequence [type=is_instance_of, input_value=['a'], input_type=list]
s1.function-after[MySequence(), json-or-python[json=list[int],python=chain[is-instance[Sequence],function-wrap[sequence_validator()]]]].0
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
"""
Доступ к имени поля¶
!!!note Это было невозможно с Pydantic V2 до V2.3, это было повторно добавлено в Pydantic V2.4.
Начиная с Pydantic V2.4, вы можете получить доступ к имени поля через handler.field_name
в __get_pydantic_core_schema__
и тем самым установить имя поля, которое будет доступно из info.field_name
.
from typing import Any
from pydantic_core import core_schema
from pydantic import BaseModel, GetCoreSchemaHandler, ValidationInfo
class CustomType:
"""Custom type that stores the field it was used in."""
def __init__(self, value: int, field_name: str):
self.value = value
self.field_name = field_name
def __repr__(self):
return f'CustomType<{self.value} {self.field_name!r}>'
@classmethod
def validate(cls, value: int, info: ValidationInfo):
return cls(value, info.field_name)
@classmethod
def __get_pydantic_core_schema__(
cls, source_type: Any, handler: GetCoreSchemaHandler
) -> core_schema.CoreSchema:
return core_schema.with_info_after_validator_function(
cls.validate, handler(int), field_name=handler.field_name
)
class MyModel(BaseModel):
my_field: CustomType
m = MyModel(my_field=1)
print(m.my_field)
#> CustomType<1 'my_field'>
Вы также можете получить доступ к field_name
из маркеров, используемых с Annotated
, например AfterValidator
.
from typing_extensions import Annotated
from pydantic import AfterValidator, BaseModel, ValidationInfo
def my_validators(value: int, info: ValidationInfo):
return f'<{value} {info.field_name!r}>'
class MyModel(BaseModel):
my_field: Annotated[int, AfterValidator(my_validators)]
m = MyModel(my_field=1)
print(m.my_field)
#> <1 'my_field'>
本文总阅读量次