प्रकार
जहां संभव हो, पाइडेंटिक फ़ील्ड को परिभाषित करने के लिए मानक लाइब्रेरी प्रकारों का उपयोग करता है, इस प्रकार सीखने की अवस्था को सुचारू करता है। हालाँकि, कई उपयोगी अनुप्रयोगों के लिए, कोई मानक लाइब्रेरी प्रकार मौजूद नहीं है, इसलिए पाइडेंटिक कई सामान्य रूप से उपयोग किए जाने वाले प्रकारों को लागू करता है।
और भी जटिल प्रकार हैं जो पाइडेंटिक एक्स्ट्रा टाइप्स पैकेज में पाए जा सकते हैं।
यदि कोई मौजूदा प्रकार आपके उद्देश्य के अनुरूप नहीं है, तो आप कस्टम गुणों और सत्यापन के साथ अपने स्वयं के पाइडेंटिक-संगत प्रकारों को भी लागू कर सकते हैं।
निम्नलिखित अनुभाग पाइडेंटिक द्वारा समर्थित प्रकारों का वर्णन करते हैं।
- मानक लाइब्रेरी प्रकार - पायथन मानक लाइब्रेरी से प्रकार।
- सख्त प्रकार - ऐसे प्रकार जो आपको संगत प्रकारों से जबरदस्ती रोकने में सक्षम बनाते हैं।
- कस्टम डेटा प्रकार - अपने स्वयं के कस्टम डेटा प्रकार बनाएं।
- फ़ील्ड प्रकार रूपांतरण - विभिन्न फ़ील्ड प्रकारों के बीच सख्त और ढीला रूपांतरण।
रूपांतरण टाइप करें¶
सत्यापन के दौरान, पाइडेंटिक डेटा को अपेक्षित प्रकारों में परिवर्तित कर सकता है।
जबरदस्ती के दो तरीके हैं: सख्त और ढीला। पाइडेंटिक सख्त और ढीले दोनों मोड में डेटा को कैसे परिवर्तित करता है, इसके बारे में अधिक जानकारी के लिए रूपांतरण तालिका देखें।
सख्त ज़बरदस्ती को सक्षम करने के विवरण के लिए सख्त मोड और सख्त प्रकार देखें।
सख्त प्रकार¶
पाइडेंटिक निम्नलिखित सख्त प्रकार प्रदान करता है:
- [
StrictBool
][पाइडेंटिक.टाइप्स.स्ट्रिक्टबूल] - [
StrictBytes
][पाइडेंटिक.टाइप्स.स्ट्रिक्टबाइट्स] - [
StrictFloat
][पाइडेंटिक.टाइप्स.स्ट्रिक्टफ्लोट] - [
StrictInt
][पाइडेंटिक.टाइप्स.स्ट्रिक्टइंट] StrictStr
ये प्रकार केवल तभी सत्यापन पास करेंगे जब मान्य मान संबंधित प्रकार का हो या उस प्रकार का उपप्रकार हो।
विवश प्रकार¶
यह व्यवहार प्रतिबंधित प्रकारों के strict
क्षेत्र के माध्यम से भी उजागर होता है और इसे कई जटिल सत्यापन नियमों के साथ जोड़ा जा सकता है। समर्थित तर्कों के लिए अलग-अलग प्रकार के हस्ताक्षर देखें।
निम्नलिखित चेतावनियाँ लागू होती हैं:
StrictBytes
(औरconbytes()
काstrict
विकल्प)bytes
औरbytearray
दोनों प्रकारों को स्वीकार करेगा।StrictInt
(औरconint()
काstrict
विकल्प)bool
प्रकारों को स्वीकार नहीं करेगा, भले हीbool
पायथन मेंint
का एक उपवर्ग है। अन्य उपवर्ग काम करेंगे.StrictFloat
(औरconfloat()
काstrict
विकल्प)int
स्वीकार नहीं करेगा।
उपरोक्त के अलावा, आपके पास एक FiniteFloat
प्रकार भी हो सकता है जो केवल परिमित मान स्वीकार करेगा (यानी inf
, -inf
या nan
नहीं)।
कस्टम प्रकार¶
आप अपने स्वयं के कस्टम डेटा प्रकार भी परिभाषित कर सकते हैं। इसे हासिल करने के कई तरीके हैं।
Annotated
के माध्यम से रचना प्रकार¶
पीईपी 593 ने Annotated
रनटाइम मेटाडेटा को प्रकारों से जोड़ने के एक तरीके के रूप में पेश किया, बिना यह बदले कि टाइप चेकर्स उनकी व्याख्या कैसे करते हैं। पाइडेंटिक इसका लाभ उठाकर आपको ऐसे प्रकार बनाने की अनुमति देता है जो टाइप चेकर्स के संबंध में मूल प्रकार के समान हैं, लेकिन सत्यापन जोड़ते हैं, अलग-अलग क्रमबद्ध करते हैं, आदि।
उदाहरण के लिए, एक सकारात्मक पूर्णांक का प्रतिनिधित्व करने वाला एक प्रकार बनाने के लिए:
# 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]
"""
ध्यान दें कि आप इसे पाइडेंटिक-अज्ञेयवादी बनाने के लिए एनोटेटेड-प्रकारों से बाधाओं का भी उपयोग कर सकते हैं:
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 स्कीमा को जोड़ या ओवरराइड कर सकते हैं:
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]
"""
नामित प्रकार के उपनाम¶
उपरोक्त उदाहरण अंतर्निहित प्रकार के उपनामों का उपयोग करते हैं। इसका मतलब यह है कि वे JSON स्कीमा में title
नहीं रख पाएंगे और उनका स्कीमा फ़ील्ड के बीच कॉपी किया जाएगा। आप नामित उपनाम बनाने के लिए इसके टाइपिंग-एक्सटेंशन बैकपोर्ट के माध्यम से पीईपी 695 के TypeAliasType
उपयोग कर सकते हैं, जिससे आप उपवर्ग बनाए बिना एक नए प्रकार को परिभाषित कर सकते हैं। यह नया प्रकार एक नाम जितना सरल हो सकता है या इसके साथ जटिल सत्यापन तर्क जुड़ा हो सकता है:
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-core
स्कीमा उत्पन्न करने का तरीका बताने के लिए एक विशेष __get_pydantic_core_schema__
लागू कर सकते हैं।
जबकि pydantic
सत्यापन और क्रमांकन को संभालने के लिए आंतरिक रूप से pydantic-core
उपयोग करता है, यह पाइडेंटिक V2 के लिए एक नया एपीआई है, इस प्रकार यह भविष्य में बदलाव किए जाने वाले क्षेत्रों में से एक है और आपको अंतर्निहित निर्माणों से चिपके रहने का प्रयास करना चाहिए जो annotated-types
, pydantic.Field
, या BeforeValidator
इत्यादि द्वारा प्रदान किए गए हैं।
आप __get_pydantic_core_schema__
कस्टम प्रकार और Annotated
में रखे जाने वाले मेटाडेटा दोनों पर लागू कर सकते हैं। दोनों ही मामलों में एपीआई मिडलवेयर जैसा है और "रैप" सत्यापनकर्ताओं के समान है: आपको एक source_type
मिलता है (जो जरूरी नहीं कि क्लास के समान हो, विशेष रूप से जेनरिक के लिए) और एक handler
जिसे आप एक प्रकार से कॉल कर सकते हैं या तो Annotated
में अगले मेटाडेटा को कॉल करने के लिए या पाइडेंटिक की आंतरिक स्कीमा पीढ़ी में कॉल करने के लिए।
सबसे सरल नो-ऑप कार्यान्वयन आपके द्वारा दिए गए प्रकार के साथ हैंडलर को कॉल करता है, फिर उसे परिणाम के रूप में लौटाता है। आप हैंडलर को कॉल करने से पहले प्रकार को संशोधित करना, हैंडलर द्वारा लौटाए गए कोर स्कीमा को संशोधित करना या हैंडलर को बिल्कुल भी कॉल न करना चुन सकते हैं।
एक कस्टम प्रकार पर एक विधि के रूप में¶
निम्नलिखित एक प्रकार का उदाहरण है जो इसे मान्य करने के तरीके को अनुकूलित करने के लिए __get_pydantic_core_schema__
उपयोग करता है। यह Pydantic V1 में __get_validators__
लागू करने के बराबर है।
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
जैसे एक संघUsername | None
त्रुटि नहीं उठाएगा.- ध्यान दें कि टाइप चेकर्स
Username
को'ABC'
निर्दिष्ट करने के बारे में शिकायत नहीं करेंगे जैसा कि उन्होंने पिछले उदाहरण में किया था क्योंकि वे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',
}
आप इस दृष्टिकोण का उपयोग उदाहरण के लिए पांडा या नम्पी प्रकारों के लिए व्यवहार को परिभाषित करने के लिए कर सकते हैं।
बॉयलरप्लेट को कम करने के लिए GetPydanticSchema
उपयोग करना¶
??? एपीआई "एपीआई दस्तावेज़ीकरण" 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
AfterValidator
औरField
जैसेAnnotated
के माध्यम से प्रकारों को अनुकूलित करने के लिए उच्च स्तरीय हुक प्रदान करता है। जब संभव हो तो इनका उपयोग करें. - हुड के तहत ये सत्यापन को अनुकूलित करने के लिए
pydantic-core
उपयोग करते हैं, और आप सीधेGetPydanticSchema
या__get_pydantic_core_schema__
के साथ एक मार्कर क्लास का उपयोग करके इसमें शामिल हो सकते हैं। - यदि आप वास्तव में एक कस्टम प्रकार चाहते हैं तो आप प्रकार पर ही
__get_pydantic_core_schema__
लागू कर सकते हैं।
कस्टम जेनेरिक कक्षाओं को संभालना¶
!!! चेतावनी यह एक उन्नत तकनीक है जिसकी आपको शुरुआत में आवश्यकता नहीं होगी। अधिकांश मामलों में आप संभवतः मानक पाइडेंटिक मॉडल के साथ ठीक रहेंगे।
आप जेनेरिक कक्षाओं को फ़ील्ड प्रकारों के रूप में उपयोग कर सकते हैं और __get_pydantic_core_schema__
के साथ "प्रकार पैरामीटर" (या उप-प्रकार) के आधार पर कस्टम सत्यापन कर सकते हैं।
यदि जेनेरिक क्लास जिसे आप उप-प्रकार के रूप में उपयोग कर रहे हैं, में क्लासमेथोड __get_pydantic_core_schema__
है, तो आपको इसे काम करने के लिए arbitrary_types_allowed
का उपयोग करने की आवश्यकता नहीं है।
क्योंकि source_type
पैरामीटर cls
पैरामीटर के समान नहीं है, आप सामान्य पैरामीटर निकालने के लिए typing.get_args
(या typing_extensions.get_args
) का उपयोग कर सकते हैं। फिर आप handler.generate_schema
पर कॉल करके उनके लिए स्कीमा तैयार करने के लिए handler
उपयोग कर सकते हैं। ध्यान दें कि हम ऐसा कुछ नहीं करते हैं 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]
"""
फ़ील्ड नाम तक पहुंच¶
!!!ध्यान दें यह Pydantic V2 से V2.3 के साथ संभव नहीं था, इसे Pydantic V2.4 में पुनः जोड़ा गया था।
Pydantic V2.4 के अनुसार, आप __get_pydantic_core_schema__
के भीतर handler.field_name
के माध्यम से फ़ील्ड नाम तक पहुंच सकते हैं और इस प्रकार फ़ील्ड नाम सेट कर सकते हैं जो 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'>
आप Annotated
के साथ उपयोग किए गए मार्करों से भी field_name
तक पहुंच सकते हैं, जैसे [AfterValidator
][pydantic.functional_validator.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'>
本文总阅读量次