Typen
Waar mogelijk gebruikt Pydantic standaardbibliotheektypen om velden te definiëren, waardoor de leercurve wordt versoepeld. Voor veel nuttige toepassingen bestaat er echter geen standaard bibliotheektype, dus implementeert Pydantic veel veelgebruikte typen.
Er zijn ook complexere typen die te vinden zijn in het Pydantic Extra Types -pakket.
Als geen enkel bestaand type geschikt is voor uw doel, kunt u ook uw eigen Pydantic-compatibele typen implementeren met aangepaste eigenschappen en validatie.
In de volgende secties worden de typen beschreven die door Pydantic worden ondersteund.
- Standaardbibliotheektypen — typen uit de standaardbibliotheek van Python.
- Strenge typen : typen waarmee u dwang van compatibele typen kunt voorkomen.
- Aangepaste gegevenstypen — maak uw eigen aangepaste gegevenstypen.
- Veldtypeconversies — strikte en lakse conversie tussen verschillende veldtypen.
Typeconversie¶
Tijdens de validatie kan Pydantic gegevens in verwachte typen dwingen.
Er zijn twee vormen van dwang: streng en laks. Zie Conversietabel voor meer details over hoe Pydantic gegevens converteert in zowel strikte als lakse modi.
Zie Strenge modus en Strenge typen voor meer informatie over het inschakelen van strikte dwang.
Strikte typen¶
Pydantic biedt de volgende strikte typen:
Deze typen komen alleen door de validatie als de gevalideerde waarde van het betreffende type is of een subtype van dat type is.
Beperkte typen¶
Dit gedrag komt ook aan het licht via het strict
veld van de beperkte typen en kan worden gecombineerd met een groot aantal complexe validatieregels. Zie de afzonderlijke typehandtekeningen voor ondersteunde argumenten.
conbytes()
condate()
- [
condecimal()
][pydantic.types.condecimaal] confloat()
confrozenset()
conint()
conlist()
conset()
constr()
De volgende voorbehouden zijn van toepassing:
StrictBytes
(en destrict
optie vanconbytes()
) accepteert zowelbytes
alsbytearray
typen.StrictInt
(en destrict
optie vanconint()
) accepteert geenbool
typen, ook al isbool
een subklasse vanint
in Python. Andere subklassen zullen werken.StrictFloat
(en destrict
optieconfloat()
) accepteert geenint
.
Naast het bovenstaande kun je ook een FiniteFloat
type hebben dat alleen eindige waarden accepteert (dwz niet inf
, -inf
of nan
).
Aangepaste typen¶
U kunt ook uw eigen aangepaste gegevenstypen definiëren. Er zijn verschillende manieren om dit te bereiken.
Typen samenstellen via Annotated
¶
PEP 593 introduceerde Annotated
als een manier om runtime-metagegevens aan typen te koppelen zonder te veranderen hoe typecheckers deze interpreteren. Pydantic maakt hiervan gebruik om u in staat te stellen typen te maken die identiek zijn aan het originele type wat betreft typecheckers, maar validatie toevoegen, anders serialiseren, enz.
Om bijvoorbeeld een type te maken dat een positieve int vertegenwoordigt:
# 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]
"""
Merk op dat u ook beperkingen van geannoteerde typen kunt gebruiken om dit Pydantic-agnostisch te maken:
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]
"""
Validatie en serialisatie toevoegen¶
U kunt validatie-, serialisatie- en JSON-schema's aan een willekeurig type toevoegen of overschrijven met behulp van de markeringen die Pydantic exporteert:
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'}
Generieke geneesmiddelen¶
U kunt typevariabelen binnen Annotated
gebruiken om herbruikbare wijzigingen aan typen aan te brengen:
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]
"""
Benoemde type-aliassen¶
In de bovenstaande voorbeelden wordt gebruik gemaakt van impliciete typealiassen. Dit betekent dat ze geen title
in JSON-schema's kunnen hebben en dat hun schema tussen velden wordt gekopieerd. U kunt PEP 695 's TypeAliasType
gebruiken via de typing-extensions backport om benoemde aliassen te maken, waardoor u een nieuw type kunt definiëren zonder subklassen te maken. Dit nieuwe type kan zo simpel zijn als een naam of er is complexe validatielogica aan verbonden:
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',
}
"""
Deze benoemde type-aliassen kunnen ook generiek zijn:
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]
"""
Benoemde recursieve typen¶
U kunt TypeAliasType
ook gebruiken om recursieve typen te maken:
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]
"""
Validatie aanpassen met __get_pydantic_core_schema__
¶
Om uitgebreidere aanpassingen uit te voeren van de manier waarop Pydantic aangepaste klassen afhandelt, en in het bijzonder wanneer u toegang hebt tot de klasse of deze kunt subclassificeren, kunt u een speciaal __get_pydantic_core_schema__
implementeren om Pydantic te vertellen hoe het pydantic-core
schema moet worden gegenereerd.
Hoewel pydantic
intern pydantic-core
gebruikt om validatie en serialisatie af te handelen, is het een nieuwe API voor Pydantic V2, dus het is een van de gebieden die in de toekomst waarschijnlijk zullen worden aangepast en je moet proberen vast te houden aan de ingebouwde constructies zoals die worden geleverd door annotated-types
, pydantic.Field
of BeforeValidator
enzovoort.
U kunt __get_pydantic_core_schema__
zowel op een aangepast type als op metagegevens implementeren die bedoeld zijn om in Annotated
te worden geplaatst. In beide gevallen is de API middleware-achtig en vergelijkbaar met die van "wrap"-validators: je krijgt een source_type
(wat niet noodzakelijkerwijs hetzelfde is als de klasse, vooral voor generieke geneesmiddelen) en een handler
die je kunt aanroepen met een type om de volgende metadata in Annotated
aan te roepen of om de interne schemageneratie van Pydantic aan te roepen.
De eenvoudigste no-op-implementatie roept de handler aan met het type dat u krijgt, en retourneert dat vervolgens als resultaat. U kunt er ook voor kiezen om het type te wijzigen voordat u de handler aanroept, het kernschema te wijzigen dat door de handler wordt geretourneerd, of de handler helemaal niet aan te roepen.
Als een methode voor een aangepast type¶
Het volgende is een voorbeeld van een type dat __get_pydantic_core_schema__
gebruikt om aan te passen hoe het wordt gevalideerd. Dit komt overeen met het implementeren __get_validators__
in 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'
Zie JSON-schema voor meer informatie over het aanpassen van JSON-schema's voor aangepaste typen.
Als annotatie¶
Vaak wilt u uw aangepaste type parametriseren met meer dan alleen generieke typeparameters (wat u kunt doen via het typesysteem en dat later zal worden besproken). Of het kan u niet echt interesseren (of willen) om een instance van uw subklasse te maken; je wilt eigenlijk het originele type, alleen met wat extra validatie.
Als u bijvoorbeeld pydantic.AfterValidator
(zie Validatie en serialisatie toevoegen ) zelf zou implementeren, zou u iets doen dat lijkt op het volgende:
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)!
- De
frozen=True
-specificatie maaktMyAfterValidator
hashbaar. Zonder dit zou een unie zoalsUsername | None
zal een fout opleveren. - Merk op dat typecontroleurs niet zullen klagen over het toewijzen van
'ABC'
aanUsername
zoals ze deden in het vorige voorbeeld, omdat zeUsername
niet als een ander type beschouwen danstr
.
Typen van derden verwerken¶
Een ander gebruiksscenario voor het patroon in de vorige sectie is het verwerken van typen van derden.
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',
}
U kunt deze aanpak gebruiken om bijvoorbeeld gedrag voor Panda's of Numpy-typen te definiëren.
Gebruik GetPydanticSchema
om de boilerplate te verminderen¶
??? api "API-documentatie" pydantic.types.GetPydanticSchema
Het zal je misschien opvallen dat de bovenstaande voorbeelden, waarin we een markerklasse maken, een flinke hoeveelheid standaardwerk vereisen. Voor veel eenvoudige gevallen kunt u dit aanzienlijk minimaliseren door pydantic.GetPydanticSchema
te gebruiken:
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'
Samenvatting¶
Laten we samenvatten:
- Pydantic biedt hooks op hoog niveau om typen aan te passen via
Annotated
zoalsAfterValidator
enField
. Gebruik deze indien mogelijk. - Onder de motorkap gebruiken deze
pydantic-core
om de validatie aan te passen, en je kunt daar rechtstreeks op aansluiten met behulp vanGetPydanticSchema
of een markerklasse met__get_pydantic_core_schema__
. - Als je echt een aangepast type wilt, kun je
__get_pydantic_core_schema__
op het type zelf implementeren.
Aangepaste generieke klassen verwerken¶
!!! waarschuwing Dit is een geavanceerde techniek die je in het begin misschien niet nodig hebt. In de meeste gevallen zult u waarschijnlijk prima uit de voeten kunnen met standaard Pydantic-modellen.
U kunt generieke klassen als veldtypen gebruiken en aangepaste validatie uitvoeren op basis van de "typeparameters" (of subtypen) met __get_pydantic_core_schema__
.
Als de Generieke klasse die u als subtype gebruikt een klassenmethode __get_pydantic_core_schema__
heeft, hoeft u arbitrary_types_allowed
niet te gebruiken om te laten werken.
Omdat de source_type
parameter niet hetzelfde is als de cls
parameter, kunt u typing.get_args
(of typing_extensions.get_args
) gebruiken om de generieke parameters te extraheren. Vervolgens kunt u de handler
gebruiken om een schema voor hen te genereren door handler.generate_schema
aan te roepen. Houd er rekening mee dat we zoiets niet doen handler(get_args(source_type)[0])
omdat we een niet-gerelateerd schema voor die generieke parameter willen genereren, en niet een schema dat wordt beïnvloed door de huidige context van Annotated
metadata en dergelijke. Dit is minder belangrijk voor aangepaste typen, maar cruciaal voor geannoteerde metagegevens die het opbouwen van schema's wijzigen.
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]
"""
Generieke containers¶
Hetzelfde idee kan worden toegepast om generieke containertypen te maken, zoals een aangepast 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]
"""
Toegang tot veldnaam¶
!!!note Dit was niet mogelijk met Pydantic V2 tot V2.3, het werd opnieuw toegevoegd in Pydantic V2.4.
Vanaf Pydantic V2.4 kunt u toegang krijgen tot de veldnaam via de handler.field_name
binnen __get_pydantic_core_schema__
en daardoor de veldnaam instellen die beschikbaar zal zijn via 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'>
U kunt ook toegang krijgen tot field_name
via de markeringen die worden gebruikt met Annotated
, zoals 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'>
本文总阅读量次