स्थगित एनोटेशन
स्थगित एनोटेशन (जैसा कि PEP563 में वर्णित है) "बस काम करें"।
from __future__ import annotations
from typing import Any
from pydantic import BaseModel
class Model(BaseModel):
a: list[int]
b: Any
print(Model(a=('1', 2, 3), b='ok'))
#> a=[1, 2, 3] b='ok'
आंतरिक रूप से, पाइडेंटिक एनोटेशन को हल करने के लिए typing.get_type_hints
के समान एक विधि को कॉल करेगा।
बिना उपयोग किये भी from __future__ import annotations
, ऐसे मामलों में जहां संदर्भित प्रकार अभी तक परिभाषित नहीं किया गया है, एक ForwardRef
या स्ट्रिंग का उपयोग किया जा सकता है:
from typing import ForwardRef
from pydantic import BaseModel
Foo = ForwardRef('Foo')
class Foo(BaseModel):
a: int = 123
b: Foo = None
print(Foo())
#> a=123 b=None
print(Foo(b={'a': '321'}))
#> a=123 b=Foo(a=321, b=None)
स्व-संदर्भित (या "पुनरावर्ती") मॉडल¶
स्व-संदर्भित फ़ील्ड वाले मॉडल भी समर्थित हैं। मॉडल निर्माण के बाद स्व-संदर्भित फ़ील्ड स्वचालित रूप से हल हो जाएंगी।
मॉडल के भीतर, आप एक स्ट्रिंग का उपयोग करके अभी तक नहीं बने मॉडल का उल्लेख कर सकते हैं:
from pydantic import BaseModel
class Foo(BaseModel):
a: int = 123
#: The sibling of `Foo` is referenced by string
sibling: 'Foo' = None
print(Foo())
#> a=123 sibling=None
print(Foo(sibling={'a': '321'}))
#> a=123 sibling=Foo(a=321, sibling=None)
यदि तुम प्रयोग करते हो from __future__ import annotations
, आप मॉडल को उसके प्रकार के नाम से भी संदर्भित कर सकते हैं:
from __future__ import annotations
from pydantic import BaseModel
class Foo(BaseModel):
a: int = 123
#: The sibling of `Foo` is referenced directly by type
sibling: Foo = None
print(Foo())
#> a=123 sibling=None
print(Foo(sibling={'a': '321'}))
#> a=123 sibling=Foo(a=321, sibling=None)
चक्रीय संदर्भ¶
स्व-संदर्भित पुनरावर्ती मॉडल के साथ काम करते समय, यह संभव है कि आपको सत्यापन इनपुट में चक्रीय संदर्भ का सामना करना पड़ सकता है। उदाहरण के लिए, यह तब हो सकता है जब विशेषताओं से बैक-रेफरेंस के साथ ओआरएम उदाहरणों को मान्य किया जाता है।
चक्रीय संदर्भों के साथ डेटा को मान्य करने का प्रयास करते समय Python RecursionError
बढ़ाने के बजाय, Pydantic चक्रीय संदर्भ का पता लगाने और एक उचित ValidationError
बढ़ाने में सक्षम है:
from typing import Optional
from pydantic import BaseModel, ValidationError
class ModelA(BaseModel):
b: 'Optional[ModelB]' = None
class ModelB(BaseModel):
a: Optional[ModelA] = None
cyclic_data = {}
cyclic_data['a'] = {'b': cyclic_data}
print(cyclic_data)
#> {'a': {'b': {...}}}
try:
ModelB.model_validate(cyclic_data)
except ValidationError as exc:
print(exc)
"""
1 validation error for ModelB
a.b
Recursion error - cyclic reference detected [type=recursion_loop, input_value={'a': {'b': {...}}}, input_type=dict]
"""
क्योंकि यह त्रुटि वास्तव में अधिकतम रिकर्सन गहराई को पार किए बिना उठाई गई है, आप सीमित शेष रिकर्सन गहराई के बारे में चिंता किए बिना उठाए गए ValidationError
पकड़ और संभाल सकते हैं:
from contextlib import contextmanager
from dataclasses import field
from typing import Iterator, List
from pydantic import BaseModel, ValidationError, field_validator
def is_recursion_validation_error(exc: ValidationError) -> bool:
errors = exc.errors()
return len(errors) == 1 and errors[0]['type'] == 'recursion_loop'
@contextmanager
def suppress_recursion_validation_error() -> Iterator[None]:
try:
yield
except ValidationError as exc:
if not is_recursion_validation_error(exc):
raise exc
class Node(BaseModel):
id: int
children: List['Node'] = field(default_factory=list)
@field_validator('children', mode='wrap')
@classmethod
def drop_cyclic_references(cls, children, h):
try:
return h(children)
except ValidationError as exc:
if not (
is_recursion_validation_error(exc)
and isinstance(children, list)
):
raise exc
value_without_cyclic_refs = []
for child in children:
with suppress_recursion_validation_error():
value_without_cyclic_refs.extend(h([child]))
return h(value_without_cyclic_refs)
# Create data with cyclic references representing the graph 1 -> 2 -> 3 -> 1
node_data = {'id': 1, 'children': [{'id': 2, 'children': [{'id': 3}]}]}
node_data['children'][0]['children'][0]['children'] = [node_data]
print(Node.model_validate(node_data))
#> id=1 children=[Node(id=2, children=[Node(id=3, children=[])])]
इसी तरह, यदि पाइडेंटिक को क्रमबद्धता के दौरान एक पुनरावर्ती संदर्भ का सामना करना पड़ता है, तो अधिकतम पुनरावर्तन गहराई के पार होने की प्रतीक्षा करने के बजाय, एक ValueError
तुरंत उठाया जाता है:
from pydantic import TypeAdapter
# Create data with cyclic references representing the graph 1 -> 2 -> 3 -> 1
node_data = {'id': 1, 'children': [{'id': 2, 'children': [{'id': 3}]}]}
node_data['children'][0]['children'][0]['children'] = [node_data]
try:
# Try serializing the circular reference as JSON
TypeAdapter(dict).dump_json(node_data)
except ValueError as exc:
print(exc)
"""
Error serializing to JSON: ValueError: Circular reference detected (id repeated)
"""
यदि चाहें तो इसे भी संभाला जा सकता है:
from dataclasses import field
from typing import Any, List
from pydantic import (
SerializerFunctionWrapHandler,
TypeAdapter,
field_serializer,
)
from pydantic.dataclasses import dataclass
@dataclass
class NodeReference:
id: int
@dataclass
class Node(NodeReference):
children: List['Node'] = field(default_factory=list)
@field_serializer('children', mode='wrap')
def serialize(
self, children: List['Node'], handler: SerializerFunctionWrapHandler
) -> Any:
"""
Serialize a list of nodes, handling circular references by excluding the children.
"""
try:
return handler(children)
except ValueError as exc:
if not str(exc).startswith('Circular reference'):
raise exc
result = []
for node in children:
try:
serialized = handler([node])
except ValueError as exc:
if not str(exc).startswith('Circular reference'):
raise exc
result.append({'id': node.id})
else:
result.append(serialized)
return result
# Create a cyclic graph:
nodes = [Node(id=1), Node(id=2), Node(id=3)]
nodes[0].children.append(nodes[1])
nodes[1].children.append(nodes[2])
nodes[2].children.append(nodes[0])
print(nodes[0])
#> Node(id=1, children=[Node(id=2, children=[Node(id=3, children=[...])])])
# Serialize the cyclic graph:
print(TypeAdapter(Node).dump_python(nodes[0]))
"""
{
'id': 1,
'children': [{'id': 2, 'children': [{'id': 3, 'children': [{'id': 1}]}]}],
}
"""
本文总阅读量次