सत्यापनकर्ता
!!! चेतावनी "🚧 कार्य प्रगति पर है" यह पृष्ठ कार्य प्रगति पर है।
यह पृष्ठ पाइडेंटिक में अधिक जटिल, कस्टम सत्यापनकर्ता बनाने के लिए उदाहरण स्निपेट प्रदान करता है।
[Annotated
][टाइपिंग.एनोटेटेड] मेटाडेटा के साथ कस्टम वैलिडेटर का उपयोग करना¶
इस उदाहरण में, हम एक कस्टम सत्यापनकर्ता का निर्माण करेंगे, जो एक [Annotated
][टाइपिंग.एनोटेटेड] प्रकार से जुड़ा होगा, जो यह सुनिश्चित करता है कि एक [datetime
][डेटटाइम.डेटटाइम] ऑब्जेक्ट किसी दिए गए टाइमज़ोन बाधा का पालन करता है।
कस्टम सत्यापनकर्ता समयक्षेत्र के स्ट्रिंग विनिर्देश का समर्थन करता है, और यदि datetime
ऑब्जेक्ट में सही समयक्षेत्र नहीं है तो एक त्रुटि उत्पन्न होगी।
हम एनोटेटेड प्रकार की स्कीमा को अनुकूलित करने के लिए सत्यापनकर्ता में __get_pydantic_core_schema__
उपयोग करते हैं (इस मामले में, datetime
), जो हमें कस्टम सत्यापन तर्क जोड़ने की अनुमति देता है। विशेष रूप से, हम एक wrap
वैलिडेटर फ़ंक्शन का उपयोग करते हैं ताकि हम [datetime
][डेटटाइम.डेटटाइम] के डिफ़ॉल्ट pydantic
सत्यापन से पहले और बाद में दोनों ऑपरेशन कर सकें।
import datetime as dt
from dataclasses import dataclass
from pprint import pprint
from typing import Any, Callable, Optional
import pytz
from pydantic_core import CoreSchema, core_schema
from typing_extensions import Annotated
from pydantic import (
GetCoreSchemaHandler,
PydanticUserError,
TypeAdapter,
ValidationError,
)
@dataclass(frozen=True)
class MyDatetimeValidator:
tz_constraint: Optional[str] = None
def tz_constraint_validator(
self,
value: dt.datetime,
handler: Callable, # (1)!
):
"""Validate tz_constraint and tz_info."""
# handle naive datetimes
if self.tz_constraint is None:
assert (
value.tzinfo is None
), 'tz_constraint is None, but provided value is tz-aware.'
return handler(value)
# validate tz_constraint and tz-aware tzinfo
if self.tz_constraint not in pytz.all_timezones:
raise PydanticUserError(
f'Invalid tz_constraint: {self.tz_constraint}',
code='unevaluable-type-annotation',
)
result = handler(value) # (2)!
assert self.tz_constraint == str(
result.tzinfo
), f'Invalid tzinfo: {str(result.tzinfo)}, expected: {self.tz_constraint}'
return result
def __get_pydantic_core_schema__(
self,
source_type: Any,
handler: GetCoreSchemaHandler,
) -> CoreSchema:
return core_schema.no_info_wrap_validator_function(
self.tz_constraint_validator,
handler(source_type),
)
LA = 'America/Los_Angeles'
ta = TypeAdapter(Annotated[dt.datetime, MyDatetimeValidator(LA)])
print(
ta.validate_python(dt.datetime(2023, 1, 1, 0, 0, tzinfo=pytz.timezone(LA)))
)
#> 2023-01-01 00:00:00-07:53
LONDON = 'Europe/London'
try:
ta.validate_python(
dt.datetime(2023, 1, 1, 0, 0, tzinfo=pytz.timezone(LONDON))
)
except ValidationError as ve:
pprint(ve.errors(), width=100)
"""
[{'ctx': {'error': AssertionError('Invalid tzinfo: Europe/London, expected: America/Los_Angeles')},
'input': datetime.datetime(2023, 1, 1, 0, 0, tzinfo=<DstTzInfo 'Europe/London' LMT-1 day, 23:59:00 STD>),
'loc': (),
'msg': 'Assertion failed, Invalid tzinfo: Europe/London, expected: America/Los_Angeles',
'type': 'assertion_error',
'url': 'https://errors.pydantic.dev/2.8/v/assertion_error'}]
"""
handler
फ़ंक्शन वह है जिसे हम मानकpydantic
सत्यापन के साथ इनपुट को सत्यापित करने के लिए कहते हैं- हम इस रैप वैलिडेटर में मानक
pydantic
सत्यापन के साथ इनपुट को सत्यापित करने के लिएhandler
फ़ंक्शन को कॉल करते हैं
हम यूटीसी ऑफसेट बाधाओं को भी इसी तरह लागू कर सकते हैं। यह मानते हुए कि हमारे पास एक lower_bound
और एक upper_bound
है, हम यह सुनिश्चित करने के लिए एक कस्टम सत्यापनकर्ता बना सकते हैं कि हमारे datetime
में एक यूटीसी ऑफसेट है जो हमारे द्वारा परिभाषित सीमा के भीतर समावेशी है:
import datetime as dt
from dataclasses import dataclass
from pprint import pprint
from typing import Any, Callable
import pytz
from pydantic_core import CoreSchema, core_schema
from typing_extensions import Annotated
from pydantic import GetCoreSchemaHandler, TypeAdapter, ValidationError
@dataclass(frozen=True)
class MyDatetimeValidator:
lower_bound: int
upper_bound: int
def validate_tz_bounds(self, value: dt.datetime, handler: Callable):
"""Validate and test bounds"""
assert value.utcoffset() is not None, 'UTC offset must exist'
assert self.lower_bound <= self.upper_bound, 'Invalid bounds'
result = handler(value)
hours_offset = value.utcoffset().total_seconds() / 3600
assert (
self.lower_bound <= hours_offset <= self.upper_bound
), 'Value out of bounds'
return result
def __get_pydantic_core_schema__(
self,
source_type: Any,
handler: GetCoreSchemaHandler,
) -> CoreSchema:
return core_schema.no_info_wrap_validator_function(
self.validate_tz_bounds,
handler(source_type),
)
LA = 'America/Los_Angeles' # UTC-7 or UTC-8
ta = TypeAdapter(Annotated[dt.datetime, MyDatetimeValidator(-10, -5)])
print(
ta.validate_python(dt.datetime(2023, 1, 1, 0, 0, tzinfo=pytz.timezone(LA)))
)
#> 2023-01-01 00:00:00-07:53
LONDON = 'Europe/London'
try:
print(
ta.validate_python(
dt.datetime(2023, 1, 1, 0, 0, tzinfo=pytz.timezone(LONDON))
)
)
except ValidationError as e:
pprint(e.errors(), width=100)
"""
[{'ctx': {'error': AssertionError('Value out of bounds')},
'input': datetime.datetime(2023, 1, 1, 0, 0, tzinfo=<DstTzInfo 'Europe/London' LMT-1 day, 23:59:00 STD>),
'loc': (),
'msg': 'Assertion failed, Value out of bounds',
'type': 'assertion_error',
'url': 'https://errors.pydantic.dev/2.8/v/assertion_error'}]
"""
नेस्टेड मॉडल फ़ील्ड्स को मान्य करना¶
यहां, हम नेस्टेड मॉडल के फ़ील्ड को सत्यापित करने के दो तरीके प्रदर्शित करते हैं, जहां सत्यापनकर्ता मूल मॉडल से डेटा का उपयोग करता है।
इस उदाहरण में, हम एक सत्यापनकर्ता का निर्माण करते हैं जो जाँचता है कि प्रत्येक उपयोगकर्ता का पासवर्ड मूल मॉडल द्वारा निर्दिष्ट निषिद्ध पासवर्ड की सूची में नहीं है।
ऐसा करने का एक तरीका बाहरी मॉडल पर एक कस्टम सत्यापनकर्ता रखना है:
from typing import List
from typing_extensions import Self
from pydantic import BaseModel, ValidationError, model_validator
class User(BaseModel):
username: str
password: str
class Organization(BaseModel):
forbidden_passwords: List[str]
users: List[User]
@model_validator(mode='after')
def validate_user_passwords(self) -> Self:
"""Check that user password is not in forbidden list. Raise a validation error if a forbidden password is encountered."""
for user in self.users:
current_pw = user.password
if current_pw in self.forbidden_passwords:
raise ValueError(
f'Password {current_pw} is forbidden. Please choose another password for user {user.username}.'
)
return self
data = {
'forbidden_passwords': ['123'],
'users': [
{'username': 'Spartacat', 'password': '123'},
{'username': 'Iceburgh', 'password': '87'},
],
}
try:
org = Organization(**data)
except ValidationError as e:
print(e)
"""
1 validation error for Organization
Value error, Password 123 is forbidden. Please choose another password for user Spartacat. [type=value_error, input_value={'forbidden_passwords': [...gh', 'password': '87'}]}, input_type=dict]
"""
वैकल्पिक रूप से, एक कस्टम सत्यापनकर्ता का उपयोग नेस्टेड मॉडल वर्ग ( User
) में किया जा सकता है, जिसमें मूल मॉडल से निषिद्ध पासवर्ड डेटा को सत्यापन संदर्भ के माध्यम से पारित किया जा सकता है।
!!! चेतावनी एक सत्यापनकर्ता के भीतर संदर्भ को बदलने की क्षमता नेस्टेड सत्यापन में बहुत अधिक शक्ति जोड़ती है, लेकिन इससे भ्रमित करने वाला या डिबग करने में कठिन कोड भी हो सकता है। इस दृष्टिकोण का प्रयोग अपने जोखिम पर करें!
from typing import List
from pydantic import BaseModel, ValidationError, ValidationInfo, field_validator
class User(BaseModel):
username: str
password: str
@field_validator('password', mode='after')
@classmethod
def validate_user_passwords(
cls, password: str, info: ValidationInfo
) -> str:
"""Check that user password is not in forbidden list."""
forbidden_passwords = (
info.context.get('forbidden_passwords', []) if info.context else []
)
if password in forbidden_passwords:
raise ValueError(f'Password {password} is forbidden.')
return password
class Organization(BaseModel):
forbidden_passwords: List[str]
users: List[User]
@field_validator('forbidden_passwords', mode='after')
@classmethod
def add_context(cls, v: List[str], info: ValidationInfo) -> List[str]:
if info.context is not None:
info.context.update({'forbidden_passwords': v})
return v
data = {
'forbidden_passwords': ['123'],
'users': [
{'username': 'Spartacat', 'password': '123'},
{'username': 'Iceburgh', 'password': '87'},
],
}
try:
org = Organization.model_validate(data, context={})
except ValidationError as e:
print(e)
"""
1 validation error for Organization
users.0.password
Value error, Password 123 is forbidden. [type=value_error, input_value='123', input_type=str]
"""
ध्यान दें कि यदि संदर्भ संपत्ति model_validate
में शामिल नहीं है, तो info.context
None
होगा और निषिद्ध पासवर्ड सूची उपरोक्त कार्यान्वयन में संदर्भ में नहीं जोड़ी जाएगी। इस प्रकार, validate_user_passwords
वांछित पासवर्ड सत्यापन नहीं करेगा।
सत्यापन संदर्भ के बारे में अधिक विवरण यहां पाया जा सकता है।
本文总阅读量次