विषय पर बढ़ें

JSON Schema

??? एपीआई "एपीआई दस्तावेज़ीकरण" pydantic.json_schema

पाइडेंटिक मॉडलों से JSON स्कीमा के स्वचालित निर्माण और अनुकूलन की अनुमति देता है। उत्पन्न JSON स्कीमा निम्नलिखित विशिष्टताओं के अनुरूप हैं:

JSON स्कीमा उत्पन्न करना

JSON स्कीमा उत्पन्न करने के लिए निम्नलिखित फ़ंक्शंस का उपयोग करें:

  • BaseModel.model_json_schema एक मॉडल के स्कीमा का jsonable निर्देश लौटाता है।
  • TypeAdapter.json_schema एक अनुकूलित प्रकार के स्कीमा का एक jsonable निर्देश देता है।

!!! ध्यान दें इन विधियों को BaseModel.model_dump_json और TypeAdapter.dump_json के साथ भ्रमित नहीं होना चाहिए, जो क्रमशः मॉडल या अनुकूलित प्रकार के उदाहरणों को क्रमबद्ध करते हैं। . ये विधियाँ JSON स्ट्रिंग लौटाती हैं। इसकी तुलना में, BaseModel.model_json_schema और TypeAdapter.json_schema क्रमशः मॉडल या अनुकूलित प्रकार के JSON स्कीमा का प्रतिनिधित्व करने वाला एक jsonable निर्देश लौटाते हैं।

!!! ध्यान दें "JSON स्कीमा की "jsonable" प्रकृति पर" model_json_schema परिणामों की "jsonable" प्रकृति के संबंध में, कॉलिंग json.dumps(m.model_json_schema()) कुछ BaseModel पर m एक वैध JSON स्ट्रिंग लौटाता है। इसी तरह, TypeAdapter.json_schema के लिए कॉलिंग json.dumps(TypeAdapter(<some_type>).json_schema()) एक वैध JSON स्ट्रिंग लौटाता है।

!!! टिप पाइडेंटिक इन दोनों के लिए समर्थन प्रदान करता है:

1. [Customizing JSON Schema](#customizing-json-schema)
2. [Customizing the JSON Schema Generation Process](#customizing-the-json-schema-generation-process)

The first approach generally has a more narrow scope, allowing for customization of the JSON schema for
more specific cases and types. The second approach generally has a more broad scope, allowing for customization
of the JSON schema generation process overall. The same effects can be achieved with either approach, but
depending on your use case, one approach might offer a more simple solution than the other.

यहां BaseModel से JSON स्कीमा उत्पन्न करने का एक उदाहरण दिया गया है:

import json
from enum import Enum
from typing import Union

from typing_extensions import Annotated

from pydantic import BaseModel, Field
from pydantic.config import ConfigDict


class FooBar(BaseModel):
    count: int
    size: Union[float, None] = None


class Gender(str, Enum):
    male = 'male'
    female = 'female'
    other = 'other'
    not_given = 'not_given'


class MainModel(BaseModel):
    """
    This is the description of the main model
    """

    model_config = ConfigDict(title='Main')

    foo_bar: FooBar
    gender: Annotated[Union[Gender, None], Field(alias='Gender')] = None
    snap: int = Field(
        42,
        title='The Snap',
        description='this is the value of snap',
        gt=30,
        lt=50,
    )


main_model_schema = MainModel.model_json_schema()  # (1)!
print(json.dumps(main_model_schema, indent=2))  # (2)!
"""
{
  "$defs": {
    "FooBar": {
      "properties": {
        "count": {
          "title": "Count",
          "type": "integer"
        },
        "size": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Size"
        }
      },
      "required": [
        "count"
      ],
      "title": "FooBar",
      "type": "object"
    },
    "Gender": {
      "enum": [
        "male",
        "female",
        "other",
        "not_given"
      ],
      "title": "Gender",
      "type": "string"
    }
  },
  "description": "This is the description of the main model",
  "properties": {
    "foo_bar": {
      "$ref": "#/$defs/FooBar"
    },
    "Gender": {
      "anyOf": [
        {
          "$ref": "#/$defs/Gender"
        },
        {
          "type": "null"
        }
      ],
      "default": null
    },
    "snap": {
      "default": 42,
      "description": "this is the value of snap",
      "exclusiveMaximum": 50,
      "exclusiveMinimum": 30,
      "title": "The Snap",
      "type": "integer"
    }
  },
  "required": [
    "foo_bar"
  ],
  "title": "Main",
  "type": "object"
}
"""
```

1. This produces a "jsonable" dict of `MainModel`'s schema.
2. Calling `json.dumps` on the schema dict produces a JSON string.

The [`TypeAdapter`][pydantic.type_adapter.TypeAdapter] class lets you create an object with methods for validating, serializing,
and producing JSON schemas for arbitrary types. This serves as a complete replacement for `schema_of` in
Pydantic V1 (which is now deprecated).

Here's an example of generating JSON schema from a [`TypeAdapter`][pydantic.type_adapter.TypeAdapter]:

```py
from typing import List

from pydantic import TypeAdapter

adapter = TypeAdapter(List[int])
print(adapter.json_schema())
#> {'items': {'type': 'integer'}, 'type': 'array'}
```

You can also generate JSON schemas for combinations of [`BaseModel`s][pydantic.main.BaseModel]
and [`TypeAdapter`s][pydantic.type_adapter.TypeAdapter], as shown in this example:

```py
import json
from typing import Union

from pydantic import BaseModel, TypeAdapter


class Cat(BaseModel):
    name: str
    color: str


class Dog(BaseModel):
    name: str
    breed: str


ta = TypeAdapter(Union[Cat, Dog])
ta_schema = ta.json_schema()
print(json.dumps(ta_schema, indent=2))
"""
{
  "$defs": {
    "Cat": {
      "properties": {
        "name": {
          "title": "Name",
          "type": "string"
        },
        "color": {
          "title": "Color",
          "type": "string"
        }
      },
      "required": [
        "name",
        "color"
      ],
      "title": "Cat",
      "type": "object"
    },
    "Dog": {
      "properties": {
        "name": {
          "title": "Name",
          "type": "string"
        },
        "breed": {
          "title": "Breed",
          "type": "string"
        }
      },
      "required": [
        "name",
        "breed"
      ],
      "title": "Dog",
      "type": "object"
    }
  },
  "anyOf": [
    {
      "$ref": "#/$defs/Cat"
    },
    {
      "$ref": "#/$defs/Dog"
    }
  ]
}
"""

JsonSchemaMode को कॉन्फ़िगर करना

model_json_schema और TypeAdapter.json_schema विधियों में mode पैरामीटर के माध्यम से JSON स्कीमा जेनरेशन का मोड निर्दिष्ट करें। डिफ़ॉल्ट रूप से, मोड 'validation' पर सेट होता है, जो मॉडल के सत्यापन स्कीमा के अनुरूप एक JSON स्कीमा उत्पन्न करता है।

JsonSchemaMode एक प्रकार का उपनाम है जो mode पैरामीटर के लिए उपलब्ध विकल्पों का प्रतिनिधित्व करता है:

  • 'validation'
  • 'serialization'

यहां एक उदाहरण दिया गया है कि mode पैरामीटर को कैसे निर्दिष्ट किया जाए और यह जेनरेट किए गए JSON स्कीमा को कैसे प्रभावित करता है:

from decimal import Decimal

from pydantic import BaseModel


class Model(BaseModel):
    a: Decimal = Decimal('12.34')


print(Model.model_json_schema(mode='validation'))
"""
{
    'properties': {
        'a': {
            'anyOf': [{'type': 'number'}, {'type': 'string'}],
            'default': '12.34',
            'title': 'A',
        }
    },
    'title': 'Model',
    'type': 'object',
}
"""

print(Model.model_json_schema(mode='serialization'))
"""
{
    'properties': {'a': {'default': '12.34', 'title': 'A', 'type': 'string'}},
    'title': 'Model',
    'type': 'object',
}
"""

JSON स्कीमा को अनुकूलित करना

उत्पन्न JSON स्कीमा को फ़ील्ड स्तर और मॉडल स्तर दोनों पर अनुकूलित किया जा सकता है:

  1. Field कंस्ट्रक्टर के साथ फ़ील्ड-स्तरीय अनुकूलन
  2. model_config के साथ मॉडल-स्तरीय अनुकूलन

फ़ील्ड और मॉडल दोनों स्तरों पर, आप JSON स्कीमा में अतिरिक्त जानकारी जोड़ने के लिए json_schema_extra विकल्प का उपयोग कर सकते हैं। नीचे json_schema_extra उपयोग करना अनुभाग इस विकल्प पर अधिक विवरण प्रदान करता है।

कस्टम प्रकारों के लिए, Pydantic JSON स्कीमा पीढ़ी को अनुकूलित करने के लिए अन्य उपकरण प्रदान करता है:

  1. WithJsonSchema एनोटेशन
  2. SkipJsonSchema एनोटेशन
  3. __get_pydantic_core_schema__ लागू करना
  4. __get_pydantic_json_schema__ लागू करना

फ़ील्ड-स्तरीय अनुकूलन

वैकल्पिक रूप से, Field फ़ंक्शन का उपयोग फ़ील्ड और सत्यापन के बारे में अतिरिक्त जानकारी प्रदान करने के लिए किया जा सकता है।

कुछ फ़ील्ड पैरामीटर विशेष रूप से जेनरेट किए गए JSON स्कीमा को अनुकूलित करने के लिए उपयोग किए जाते हैं:

  • title : फ़ील्ड का शीर्षक.
  • description : क्षेत्र का विवरण.
  • examples : क्षेत्र के उदाहरण.
  • json_schema_extra : अतिरिक्त JSON स्कीमा गुण फ़ील्ड में जोड़े जाएंगे।
  • field_title_generator : एक फ़ंक्शन जो प्रोग्रामेटिक रूप से फ़ील्ड का शीर्षक उसके नाम और जानकारी के आधार पर सेट करता है।

यहाँ एक उदाहरण है:

import json

from pydantic import BaseModel, EmailStr, Field, SecretStr


class User(BaseModel):
    age: int = Field(description='Age of the user')
    email: EmailStr = Field(examples=['marcelo@mail.com'])
    name: str = Field(title='Username')
    password: SecretStr = Field(
        json_schema_extra={
            'title': 'Password',
            'description': 'Password of the user',
            'examples': ['123456'],
        }
    )


print(json.dumps(User.model_json_schema(), indent=2))
"""
{
  "properties": {
    "age": {
      "description": "Age of the user",
      "title": "Age",
      "type": "integer"
    },
    "email": {
      "examples": [
        "marcelo@mail.com"
      ],
      "format": "email",
      "title": "Email",
      "type": "string"
    },
    "name": {
      "title": "Username",
      "type": "string"
    },
    "password": {
      "description": "Password of the user",
      "examples": [
        "123456"
      ],
      "format": "password",
      "title": "Password",
      "type": "string",
      "writeOnly": true
    }
  },
  "required": [
    "age",
    "email",
    "name",
    "password"
  ],
  "title": "User",
  "type": "object"
}
"""

अप्रवर्तनीय Field बाधाएँ

यदि पाइडेंटिक को ऐसी बाधाएं मिलती हैं जिन्हें लागू नहीं किया जा रहा है, तो एक त्रुटि उत्पन्न हो जाएगी। यदि आप बाधा को स्कीमा में प्रकट होने के लिए मजबूर करना चाहते हैं, भले ही इसे पार्सिंग पर चेक नहीं किया जा रहा हो, तो आप कच्चे स्कीमा विशेषता नाम के साथ Field में विविध तर्कों का उपयोग कर सकते हैं:

from pydantic import BaseModel, Field, PositiveInt

try:
    # this won't work since `PositiveInt` takes precedence over the
    # constraints defined in `Field`, meaning they're ignored
    class Model(BaseModel):
        foo: PositiveInt = Field(..., lt=10)

except ValueError as e:
    print(e)


# if you find yourself needing this, an alternative is to declare
# the constraints in `Field` (or you could use `conint()`)
# here both constraints will be enforced:
class ModelB(BaseModel):
    # Here both constraints will be applied and the schema
    # will be generated correctly
    foo: int = Field(..., gt=0, lt=10)


print(ModelB.model_json_schema())
"""
{
    'properties': {
        'foo': {
            'exclusiveMaximum': 10,
            'exclusiveMinimum': 0,
            'title': 'Foo',
            'type': 'integer',
        }
    },
    'required': ['foo'],
    'title': 'ModelB',
    'type': 'object',
}
"""

आप Field कंस्ट्रक्टर के माध्यम से typing.Annotated के माध्यम से JSON स्कीमा संशोधन निर्दिष्ट कर सकते हैं:

import json
from uuid import uuid4

from typing_extensions import Annotated

from pydantic import BaseModel, Field


class Foo(BaseModel):
    id: Annotated[str, Field(default_factory=lambda: uuid4().hex)]
    name: Annotated[str, Field(max_length=256)] = Field(
        'Bar', title='CustomName'
    )


print(json.dumps(Foo.model_json_schema(), indent=2))
"""
{
  "properties": {
    "id": {
      "title": "Id",
      "type": "string"
    },
    "name": {
      "default": "Bar",
      "maxLength": 256,
      "title": "CustomName",
      "type": "string"
    }
  },
  "title": "Foo",
  "type": "object"
}
"""

प्रोग्रामेटिक फ़ील्ड शीर्षक निर्माण

field_title_generator पैरामीटर का उपयोग किसी फ़ील्ड के नाम और जानकारी के आधार पर प्रोग्रामेटिक रूप से शीर्षक उत्पन्न करने के लिए किया जा सकता है।

निम्नलिखित उदाहरण देखें:

import json

from pydantic import BaseModel, Field
from pydantic.fields import FieldInfo


def make_title(field_name: str, field_info: FieldInfo) -> str:
    return field_name.upper()


class Person(BaseModel):
    name: str = Field(field_title_generator=make_title)
    age: int = Field(field_title_generator=make_title)


print(json.dumps(Person.model_json_schema(), indent=2))
"""
{
  "properties": {
    "name": {
      "title": "NAME",
      "type": "string"
    },
    "age": {
      "title": "AGE",
      "type": "integer"
    }
  },
  "required": [
    "name",
    "age"
  ],
  "title": "Person",
  "type": "object"
}
"""

मॉडल-स्तरीय अनुकूलन

आप किसी मॉडल पर JSON स्कीमा पीढ़ी को अनुकूलित करने के लिए मॉडल कॉन्फिग का भी उपयोग कर सकते हैं। विशेष रूप से, निम्नलिखित कॉन्फ़िगरेशन विकल्प प्रासंगिक हैं:

json_schema_extra उपयोग करना

json_schema_extra विकल्प का उपयोग JSON स्कीमा में अतिरिक्त जानकारी जोड़ने के लिए किया जा सकता है, या तो फ़ील्ड स्तर पर या मॉडल स्तर पर। आप json_schema_extra पर एक dict या Callable पास कर सकते हैं।

एक dict के साथ json_schema_extra उपयोग करना

आप JSON स्कीमा में अतिरिक्त जानकारी जोड़ने के लिए json_schema_extra पर एक dict पारित कर सकते हैं:

import json

from pydantic import BaseModel, ConfigDict


class Model(BaseModel):
    a: str

    model_config = ConfigDict(json_schema_extra={'examples': [{'a': 'Foo'}]})


print(json.dumps(Model.model_json_schema(), indent=2))
"""
{
  "examples": [
    {
      "a": "Foo"
    }
  ],
  "properties": {
    "a": {
      "title": "A",
      "type": "string"
    }
  },
  "required": [
    "a"
  ],
  "title": "Model",
  "type": "object"
}
"""

Callable के साथ json_schema_extra उपयोग करना

आप किसी फ़ंक्शन के साथ JSON स्कीमा को संशोधित करने के लिए json_schema_extra पर Callable पास कर सकते हैं:

import json

from pydantic import BaseModel, Field


def pop_default(s):
    s.pop('default')


class Model(BaseModel):
    a: int = Field(default=1, json_schema_extra=pop_default)


print(json.dumps(Model.model_json_schema(), indent=2))
"""
{
  "properties": {
    "a": {
      "title": "A",
      "type": "integer"
    }
  },
  "title": "Model",
  "type": "object"
}
"""

json_schema_extra मर्ज किया जा रहा है

v2.9 से प्रारंभ करके, Pydantic एनोटेटेड प्रकारों से json_schema_extra शब्दकोशों का विलय करता है। यह पैटर्न पिछले ओवरराइड व्यवहार के बजाय विलय के लिए अधिक योगात्मक दृष्टिकोण प्रदान करता है। यह कई प्रकारों में json स्कीमा अतिरिक्त जानकारी का पुन: उपयोग करने के मामलों में काफी मददगार हो सकता है।

हमने इस बदलाव को बड़े पैमाने पर बग फिक्स के रूप में देखा, क्योंकि यह BaseModel और TypeAdapter इंस्टेंसेस के बीच json_schema_extra विलय व्यवहार में अनजाने मतभेदों को हल करता है - अधिक विवरण के लिए इस मुद्दे को देखें।

import json

from typing_extensions import Annotated, TypeAlias

from pydantic import Field, TypeAdapter

ExternalType: TypeAlias = Annotated[
    int, Field(..., json_schema_extra={'key1': 'value1'})
]

ta = TypeAdapter(
    Annotated[ExternalType, Field(..., json_schema_extra={'key2': 'value2'})]
)
print(json.dumps(ta.json_schema(), indent=2))
"""
{
  "key1": "value1",
  "key2": "value2",
  "type": "integer"
}
"""

यदि आप अपने अंतिम json_schema_extra विनिर्देशों के लिए पिछले वाले को ओवरराइड करना पसंद करते हैं, तो आप अधिक महत्वपूर्ण परिवर्तन करने के लिए callable उपयोग कर सकते हैं, जिसमें कुंजियाँ जोड़ना या हटाना, या मानों को संशोधित करना शामिल है। यदि आप Pydantic v2.8 और इससे पहले के संस्करण में मौजूद json_schema_extra ओवरराइड के व्यवहार की नकल करना चाहते हैं तो आप इस पैटर्न का उपयोग कर सकते हैं:

import json

from typing_extensions import Annotated, TypeAlias

from pydantic import Field, TypeAdapter
from pydantic.json_schema import JsonDict

ExternalType: TypeAlias = Annotated[
    int, Field(..., json_schema_extra={'key1': 'value1', 'key2': 'value2'})
]


def finalize_schema(s: JsonDict) -> None:
    s.pop('key1')
    s['key2'] = s['key2'] + '-final'
    s['key3'] = 'value3-final'


ta = TypeAdapter(
    Annotated[ExternalType, Field(..., json_schema_extra=finalize_schema)]
)
print(json.dumps(ta.json_schema(), indent=2))
"""
{
  "key2": "value2-final",
  "key3": "value3-final",
  "type": "integer"
}
"""

WithJsonSchema एनोटेशन

??? एपीआई "एपीआई दस्तावेज़ीकरण" pydantic.json_schema.WithJsonSchema

!!! टिप कस्टम प्रकारों के लिए __get_pydantic_json_schema__ लागू करने की तुलना में WithJsonSchema] का उपयोग करना बेहतर है, क्योंकि यह अधिक सरल और कम त्रुटि-प्रवण है।

WithJsonSchema एनोटेशन का उपयोग किसी दिए गए प्रकार के लिए जेनरेट किए गए (बेस) JSON स्कीमा को ओवरराइड करने के लिए किया जा सकता है, बिना प्रकार पर __get_pydantic_core_schema__ या __get_pydantic_json_schema__ को लागू करने की आवश्यकता के बिना।

यह उन प्रकारों के लिए JSON स्कीमा सेट करने का एक तरीका प्रदान करता है जो अन्यथा JSON स्कीमा बनाते समय त्रुटियां उत्पन्न कर सकते हैं, जैसे कि Callable , या वे प्रकार जिनमें is-instance कोर स्कीमा है।

उदाहरण के लिए, निम्नलिखित उदाहरण में [PlainValidator][pydantic.functional_validator.PlainValidator] का उपयोग अन्यथा JSON स्कीमा बनाते समय एक त्रुटि उत्पन्न करेगा क्योंकि [PlainValidator][pydantic.functional_validator.PlainValidator] एक Callable है। हालाँकि, WithJsonSchema एनोटेशन का उपयोग करके, हम कस्टम MyInt प्रकार के लिए जेनरेट किए गए JSON स्कीमा को ओवरराइड कर सकते हैं:

import json

from typing_extensions import Annotated

from pydantic import BaseModel, PlainValidator, WithJsonSchema

MyInt = Annotated[
    int,
    PlainValidator(lambda v: int(v) + 1),
    WithJsonSchema({'type': 'integer', 'examples': [1, 0, -1]}),
]


class Model(BaseModel):
    a: MyInt


print(Model(a='1').a)
#> 2

print(json.dumps(Model.model_json_schema(), indent=2))
"""
{
  "properties": {
    "a": {
      "examples": [
        1,
        0,
        -1
      ],
      "title": "A",
      "type": "integer"
    }
  },
  "required": [
    "a"
  ],
  "title": "Model",
  "type": "object"
}
"""

!!! नोट जैसा कि इस मुद्दे में चर्चा की गई है, भविष्य में, यह संभावना है कि Pydantic [PlainValidator][pydantic.functional_validator.PlainValidator] जैसे प्रकारों के लिए JSON स्कीमा पीढ़ी के लिए अंतर्निहित समर्थन जोड़ देगा, लेकिन WithJsonSchema एनोटेशन अन्य कस्टम प्रकारों के लिए अभी भी उपयोगी होगा।

SkipJsonSchema एनोटेशन

??? एपीआई "एपीआई दस्तावेज़ीकरण" pydantic.json_schema.SkipJsonSchema

SkipJsonSchema एनोटेशन का उपयोग जनरेट किए गए JSON स्कीमा से एक सम्मिलित फ़ील्ड (या फ़ील्ड के विनिर्देशों का हिस्सा) को छोड़ने के लिए किया जा सकता है। अधिक विवरण के लिए एपीआई दस्तावेज़ देखें।

__get_pydantic_core_schema__ लागू करना

कस्टम प्रकार ( field_name: TheType या field_name: Annotated[TheType, ...] ) साथ ही Annotated मेटाडेटा (के रूप में उपयोग किया जाता है)। field_name: Annotated[int, SomeMetadata] ) __get_pydantic_core_schema__ लागू करके उत्पन्न स्कीमा को संशोधित या ओवरराइड कर सकता है। यह विधि दो स्थितीय तर्क प्राप्त करती है:

  1. प्रकार एनोटेशन जो इस प्रकार से मेल खाता है (इसलिए TheType[T][int] के मामले में यह TheType[int] होगा)।
  2. __get_pydantic_core_schema__ के अगले कार्यान्वयनकर्ता को कॉल करने के लिए एक हैंडलर/कॉलबैक।

हैंडलर सिस्टम बिल्कुल mode='wrap' सत्यापनकर्ताओं की तरह काम करता है। इस मामले में इनपुट प्रकार है और आउटपुट core_schema है।

यहां एक कस्टम प्रकार का उदाहरण दिया गया है जो जेनरेट किए गए core_schema ओवरराइड करता है :

from dataclasses import dataclass
from typing import Any, Dict, List, Type

from pydantic_core import core_schema

from pydantic import BaseModel, GetCoreSchemaHandler


@dataclass
class CompressedString:
    dictionary: Dict[int, str]
    text: List[int]

    def build(self) -> str:
        return ' '.join([self.dictionary[key] for key in self.text])

    @classmethod
    def __get_pydantic_core_schema__(
        cls, source: Type[Any], handler: GetCoreSchemaHandler
    ) -> core_schema.CoreSchema:
        assert source is CompressedString
        return core_schema.no_info_after_validator_function(
            cls._validate,
            core_schema.str_schema(),
            serialization=core_schema.plain_serializer_function_ser_schema(
                cls._serialize,
                info_arg=False,
                return_schema=core_schema.str_schema(),
            ),
        )

    @staticmethod
    def _validate(value: str) -> 'CompressedString':
        inverse_dictionary: Dict[str, int] = {}
        text: List[int] = []
        for word in value.split(' '):
            if word not in inverse_dictionary:
                inverse_dictionary[word] = len(inverse_dictionary)
            text.append(inverse_dictionary[word])
        return CompressedString(
            {v: k for k, v in inverse_dictionary.items()}, text
        )

    @staticmethod
    def _serialize(value: 'CompressedString') -> str:
        return value.build()


class MyModel(BaseModel):
    value: CompressedString


print(MyModel.model_json_schema())
"""
{
    'properties': {'value': {'title': 'Value', 'type': 'string'}},
    'required': ['value'],
    'title': 'MyModel',
    'type': 'object',
}
"""
print(MyModel(value='fox fox fox dog fox'))
"""
value = CompressedString(dictionary={0: 'fox', 1: 'dog'}, text=[0, 0, 0, 1, 0])
"""

print(MyModel(value='fox fox fox dog fox').model_dump(mode='json'))
#> {'value': 'fox fox fox dog fox'}

चूँकि Pydantic को नहीं पता कि CompressedString के लिए स्कीमा कैसे तैयार किया जाए, यदि आप इसकी __get_pydantic_core_schema__ विधि में handler(source) कॉल करते हैं तो आपको एक मिलेगा pydantic.errors.PydanticSchemaGenerationError गलती। अधिकांश कस्टम प्रकारों के लिए यही स्थिति होगी, इसलिए आप लगभग कभी भी कस्टम प्रकारों के लिए handler को कॉल नहीं करना चाहेंगे।

Annotated मेटाडेटा के लिए प्रक्रिया काफी हद तक समान है, सिवाय इसके कि आप आम तौर पर स्कीमा उत्पन्न करने वाले पाइडेंटिक हैंडल को handler में कॉल कर सकते हैं।

from dataclasses import dataclass
from typing import Any, Sequence, Type

from pydantic_core import core_schema
from typing_extensions import Annotated

from pydantic import BaseModel, GetCoreSchemaHandler, ValidationError


@dataclass
class RestrictCharacters:
    alphabet: Sequence[str]

    def __get_pydantic_core_schema__(
        self, source: Type[Any], handler: GetCoreSchemaHandler
    ) -> core_schema.CoreSchema:
        if not self.alphabet:
            raise ValueError('Alphabet may not be empty')
        schema = handler(
            source
        )  # get the CoreSchema from the type / inner constraints
        if schema['type'] != 'str':
            raise TypeError('RestrictCharacters can only be applied to strings')
        return core_schema.no_info_after_validator_function(
            self.validate,
            schema,
        )

    def validate(self, value: str) -> str:
        if any(c not in self.alphabet for c in value):
            raise ValueError(
                f'{value!r} is not restricted to {self.alphabet!r}'
            )
        return value


class MyModel(BaseModel):
    value: Annotated[str, RestrictCharacters('ABC')]


print(MyModel.model_json_schema())
"""
{
    'properties': {'value': {'title': 'Value', 'type': 'string'}},
    'required': ['value'],
    'title': 'MyModel',
    'type': 'object',
}
"""
print(MyModel(value='CBA'))
#> value='CBA'

try:
    MyModel(value='XYZ')
except ValidationError as e:
    print(e)
    """
    1 validation error for MyModel
    value
      Value error, 'XYZ' is not restricted to 'ABC' [type=value_error, input_value='XYZ', input_type=str]
    """

अब तक हम स्कीमा को लपेट रहे हैं, लेकिन यदि आप इसे संशोधित करना चाहते हैं या इसे अनदेखा करना चाहते हैं तो आप ऐसा भी कर सकते हैं।

स्कीमा को संशोधित करने के लिए, पहले हैंडलर को कॉल करें, फिर परिणाम को बदलें:

from typing import Any, Type

from pydantic_core import ValidationError, core_schema
from typing_extensions import Annotated

from pydantic import BaseModel, GetCoreSchemaHandler


class SmallString:
    def __get_pydantic_core_schema__(
        self,
        source: Type[Any],
        handler: GetCoreSchemaHandler,
    ) -> core_schema.CoreSchema:
        schema = handler(source)
        assert schema['type'] == 'str'
        schema['max_length'] = 10  # modify in place
        return schema


class MyModel(BaseModel):
    value: Annotated[str, SmallString()]


try:
    MyModel(value='too long!!!!!')
except ValidationError as e:
    print(e)
    """
    1 validation error for MyModel
    value
      String should have at most 10 characters [type=string_too_long, input_value='too long!!!!!', input_type=str]
    """

!!! टिप ध्यान दें कि आपको एक स्कीमा वापस करना होगा , भले ही आप इसे उसी स्थान पर परिवर्तित कर रहे हों।

स्कीमा को पूरी तरह से ओवरराइड करने के लिए, हैंडलर को कॉल न करें और अपना स्वयं का CoreSchema वापस लौटाएँ:

from typing import Any, Type

from pydantic_core import ValidationError, core_schema
from typing_extensions import Annotated

from pydantic import BaseModel, GetCoreSchemaHandler


class AllowAnySubclass:
    def __get_pydantic_core_schema__(
        self, source: Type[Any], handler: GetCoreSchemaHandler
    ) -> core_schema.CoreSchema:
        # we can't call handler since it will fail for arbitrary types
        def validate(value: Any) -> Any:
            if not isinstance(value, source):
                raise ValueError(
                    f'Expected an instance of {source}, got an instance of {type(value)}'
                )

        return core_schema.no_info_plain_validator_function(validate)


class Foo:
    pass


class Model(BaseModel):
    f: Annotated[Foo, AllowAnySubclass()]


print(Model(f=Foo()))
#> f=None


class NotFoo:
    pass


try:
    Model(f=NotFoo())
except ValidationError as e:
    print(e)
    """
    1 validation error for Model
    f
      Value error, Expected an instance of <class '__main__.Foo'>, got an instance of <class '__main__.NotFoo'> [type=value_error, input_value=<__main__.NotFoo object at 0x0123456789ab>, input_type=NotFoo]
    """

जैसा कि ऊपर देखा गया है, BaseModel प्रकार के साथ किसी फ़ील्ड को एनोटेट करने का उपयोग जेनरेट किए गए जेसन स्कीमा को संशोधित या ओवरराइड करने के लिए किया जा सकता है। हालाँकि, यदि आप Annotated के माध्यम से मेटाडेटा संग्रहीत करने का लाभ लेना चाहते हैं, लेकिन आप जेनरेट किए गए JSON स्कीमा को ओवरराइड नहीं करना चाहते हैं, तो आप मेटाडेटा क्लास पर लागू __get_pydantic_core_schema__ के नो-ऑप संस्करण के साथ निम्नलिखित दृष्टिकोण का उपयोग कर सकते हैं:

from typing import Type

from pydantic_core import CoreSchema
from typing_extensions import Annotated

from pydantic import BaseModel, GetCoreSchemaHandler


class Metadata(BaseModel):
    foo: str = 'metadata!'
    bar: int = 100

    @classmethod
    def __get_pydantic_core_schema__(
        cls, source_type: Type[BaseModel], handler: GetCoreSchemaHandler
    ) -> CoreSchema:
        if cls is not source_type:
            return handler(source_type)
        return super().__get_pydantic_core_schema__(source_type, handler)


class Model(BaseModel):
    state: Annotated[int, Metadata()]


m = Model.model_validate({'state': 2})
print(repr(m))
#> Model(state=2)
print(m.model_fields)
"""
{
    'state': FieldInfo(
        annotation=int,
        required=True,
        metadata=[Metadata(foo='metadata!', bar=100)],
    )
}
"""

__get_pydantic_json_schema__ लागू करना

आप जेनरेट किए गए json स्कीमा को संशोधित या ओवरराइड करने के लिए __get_pydantic_json_schema__ भी लागू कर सकते हैं। इस विधि को संशोधित करने से केवल JSON स्कीमा प्रभावित होता है - यह कोर स्कीमा को प्रभावित नहीं करता है, जिसका उपयोग सत्यापन और क्रमबद्धता के लिए किया जाता है।

यहां जेनरेटेड JSON स्कीमा को संशोधित करने का एक उदाहरण दिया गया है:

import json
from typing import Any

from pydantic_core import core_schema as cs

from pydantic import GetCoreSchemaHandler, GetJsonSchemaHandler, TypeAdapter
from pydantic.json_schema import JsonSchemaValue


class Person:
    name: str
    age: int

    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

    @classmethod
    def __get_pydantic_core_schema__(
        cls, source_type: Any, handler: GetCoreSchemaHandler
    ) -> cs.CoreSchema:
        return cs.typed_dict_schema(
            {
                'name': cs.typed_dict_field(cs.str_schema()),
                'age': cs.typed_dict_field(cs.int_schema()),
            },
        )

    @classmethod
    def __get_pydantic_json_schema__(
        cls, core_schema: cs.CoreSchema, handler: GetJsonSchemaHandler
    ) -> JsonSchemaValue:
        json_schema = handler(core_schema)
        json_schema = handler.resolve_ref_schema(json_schema)
        json_schema['examples'] = [
            {
                'name': 'John Doe',
                'age': 25,
            }
        ]
        json_schema['title'] = 'Person'
        return json_schema


print(json.dumps(TypeAdapter(Person).json_schema(), indent=2))
"""
{
  "examples": [
    {
      "age": 25,
      "name": "John Doe"
    }
  ],
  "properties": {
    "name": {
      "title": "Name",
      "type": "string"
    },
    "age": {
      "title": "Age",
      "type": "integer"
    }
  },
  "required": [
    "name",
    "age"
  ],
  "title": "Person",
  "type": "object"
}
"""

field_title_generator उपयोग करना

field_title_generator पैरामीटर का उपयोग किसी फ़ील्ड के नाम और जानकारी के आधार पर प्रोग्रामेटिक रूप से शीर्षक उत्पन्न करने के लिए किया जा सकता है। यह फ़ील्ड स्तर field_title_generator के समान है, लेकिन ConfigDict विकल्प कक्षा के सभी फ़ील्ड पर लागू किया जाएगा।

निम्नलिखित उदाहरण देखें:

import json

from pydantic import BaseModel, ConfigDict


class Person(BaseModel):
    model_config = ConfigDict(
        field_title_generator=lambda field_name, field_info: field_name.upper()
    )
    name: str
    age: int


print(json.dumps(Person.model_json_schema(), indent=2))
"""
{
  "properties": {
    "name": {
      "title": "NAME",
      "type": "string"
    },
    "age": {
      "title": "AGE",
      "type": "integer"
    }
  },
  "required": [
    "name",
    "age"
  ],
  "title": "Person",
  "type": "object"
}
"""

model_title_generator उपयोग करना

model_title_generator कॉन्फ़िगरेशन विकल्प field_title_generator विकल्प के समान है, लेकिन यह मॉडल के शीर्षक पर ही लागू होता है, और मॉडल वर्ग को इनपुट के रूप में स्वीकार करता है।

निम्नलिखित उदाहरण देखें:

import json
from typing import Type

from pydantic import BaseModel, ConfigDict


def make_title(model: Type) -> str:
    return f'Title-{model.__name__}'


class Person(BaseModel):
    model_config = ConfigDict(model_title_generator=make_title)
    name: str
    age: int


print(json.dumps(Person.model_json_schema(), indent=2))
"""
{
  "properties": {
    "name": {
      "title": "Name",
      "type": "string"
    },
    "age": {
      "title": "Age",
      "type": "integer"
    }
  },
  "required": [
    "name",
    "age"
  ],
  "title": "Title-Person",
  "type": "object"
}
"""

JSON स्कीमा प्रकार

प्रकार, कस्टम फ़ील्ड प्रकार और बाधाएं (जैसे max_length ) को निम्नलिखित प्राथमिकता क्रम में संबंधित विशिष्ट प्रारूपों में मैप किया जाता है (जब कोई समकक्ष उपलब्ध हो):

  1. JSON स्कीमा कोर
  2. JSON स्कीमा सत्यापन
  3. ओपनएपीआई डेटा प्रकार
  4. अधिक जटिल string उप-प्रकारों के लिए पाइडेंटिक एक्सटेंशन को परिभाषित करने के लिए मानक format JSON फ़ील्ड का उपयोग किया जाता है।

Python या Pydantic से JSON स्कीमा तक फ़ील्ड स्कीमा मैपिंग इस प्रकार की जाती है:

शीर्ष-स्तरीय स्कीमा पीढ़ी

आप एक शीर्ष-स्तरीय JSON स्कीमा भी तैयार कर सकते हैं जिसमें केवल मॉडल और संबंधित की सूची शामिल है इसके ` $defs` में उप-मॉडल: ```py आउटपुट='json' pydantic से json आयात करें, pydantic से बेसमॉडल आयात करें। pydantic.json_schema से आयात मॉडल_json_schema वर्ग Foo(BaseModel): a: str = कोई नहीं वर्ग मॉडल(BaseModel): b: फू क्लास बार(बेसमॉडल): सी: int _, टॉप_लेवल_स्कीमा = मॉडल_जसन_स्कीमा( [(मॉडल, 'सत्यापन'), (बार, 'सत्यापन')], शीर्षक='मेरी स्कीमा' ) प्रिंट(json.dumps(top_level_schema, indent=2)) """ { "$ defs": { "छड़": { "गुण": { "सी": { "शीर्षक": "सी", "प्रकार": "पूर्णांक" } }, "आवश्यक": [ "सी" ], "शीर्षक टाईटल", "प्रकार": "ऑब्जेक्ट" }, "फू": { "गुण": { "ए": { "डिफ़ॉल्ट": शून्य, "शीर्षक": "ए", "प्रकार": "स्ट्रिंग" } }, "शीर्षक": "फू", "प्रकार": "ऑब्जेक्ट" }, "नमूना": { "गुण": { "बी": { " $defs": { "FooBar": { "properties": { "count": { "title": "Count", "type": "integer" }, "size": { "anyOf": [ { "type": "number" }, { "type": "null" } ], "default": null, "title": "Size" } }, "required": [ "count" ], "title": "FooBar", "type": "object" }, "Gender": { "enum": [ "male", "female", "other", "not_given" ], "title": "Gender", "type": "string" } }, "description": "This is the description of the main model", "properties": { "foo_bar": { "$ डीईएफ़/फू" } }, "आवश्यक": [ "बी" ], "शीर्षक": "मॉडल", "प्रकार": "ऑब्जेक्ट" } }, "शीर्षक": "मेरी स्कीमा" } """

## Customizing the JSON Schema Generation Process

??? api "API Documentation"
    [`pydantic.json_schema`][pydantic.json_schema.GenerateJsonSchema]<br>

If you need custom schema generation, you can use a `schema_generator`, modifying the
[`GenerateJsonSchema`][pydantic.json_schema.GenerateJsonSchema] class as necessary for your application.

The various methods that can be used to produce JSON schema accept a keyword argument `schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema`, and you can pass your custom subclass to these methods in order to use your own approach to generating JSON schema.

`GenerateJsonSchema` implements the translation of a type's `pydantic-core` schema into a JSON schema.
By design, this class breaks the JSON schema generation process into smaller methods that can be easily overridden in
subclasses to modify the "global" approach to generating JSON schema.

```

py from pydantic import BaseModel from pydantic.json_schema import GenerateJsonSchema

class MyGenerateJsonSchema(GenerateJsonSchema):
    def generate(self, schema, mode='validation'):
        json_schema = super().generate(schema, mode=mode)
        json_schema['title'] = 'Customize title'
        json_schema['$schema'] = self.schema_dialect
        return json_schema


class MyModel(BaseModel):
    x: int


print(MyModel.model_json_schema(schema_generator=MyGenerateJsonSchema))
"""
{
    'properties': {'x': {'title': 'X', 'type': 'integer'}},
    'required': ['x'],
    'title': 'Customize title',
    'type': 'object',
    '$schema': 'https://json-schema.org/draft/2020-12/schema',
}
"""

नीचे एक तरीका है जिसका उपयोग आप स्कीमा से किसी भी फ़ील्ड को बाहर करने के लिए कर सकते हैं जिसमें वैध json स्कीमा नहीं है:

from typing import Callable

from pydantic_core import PydanticOmit, core_schema

from pydantic import BaseModel
from pydantic.json_schema import GenerateJsonSchema, JsonSchemaValue


class MyGenerateJsonSchema(GenerateJsonSchema):
    def handle_invalid_for_json_schema(
        self, schema: core_schema.CoreSchema, error_info: str
    ) -> JsonSchemaValue:
        raise PydanticOmit


def example_callable():
    return 1


class Example(BaseModel):
    name: str = 'example'
    function: Callable = example_callable


instance_example = Example()

validation_schema = instance_example.model_json_schema(
    schema_generator=MyGenerateJsonSchema, mode='validation'
)
print(validation_schema)
"""
{
    'properties': {
        'name': {'default': 'example', 'title': 'Name', 'type': 'string'}
    },
    'title': 'Example',
    'type': 'object',
}
"""

को अनुकूलित करना $ref`s in JSON Schema The format of `$ref s को model_json_schema() पर कॉल करके बदला जा सकता है

या model_dump_json() ref_template कीवर्ड तर्क के साथ। परिभाषाएँ हमेशा कुंजी के अंतर्गत संग्रहीत की जाती हैं $defs`, but a specified prefix can be used for the references. This is useful if you need to extend or modify the JSON schema default definitions location. For example, with OpenAPI: ```py output="json" import json from pydantic import BaseModel from pydantic.type_adapter import TypeAdapter class Foo(BaseModel): a: int class Model(BaseModel): a: Foo adapter = TypeAdapter(Model) print( json.dumps( adapter.json_schema(ref_template='#/components/schemas/{model}'), indent=2, ) ) """ { "$defs": { "Foo": { "properties": { "a": { "title": "A", "type": "integer" } }, "required": [ "a" ], "title": "Foo", "type": "object" } }, "properties": { "a": { "$ref": "#/components/schemas/Foo" } }, "required": [ "a" ], "title": "Model", "type": "object" } """ ``` ## Miscellaneous Notes on JSON Schema Generation * The JSON schema for `Optional` fields indicates that the value `null` is allowed. * The `Decimal` type is exposed in JSON schema (and serialized) as a string. * Since the `namedtuple` type doesn't exist in JSON, a model's JSON schema does not preserve `namedtuple`s as `namedtuple`s. * Sub-models used are added to the `$defs विशिष्टता के अनुसार JSON विशेषता और संदर्भित।

  • कस्टम शीर्षक, विवरण या डिफ़ॉल्ट मान जैसे संशोधनों वाले उप-मॉडल ( Field वर्ग के माध्यम से), संदर्भित के बजाय पुनरावर्ती रूप से शामिल किए जाते हैं।
  • मॉडलों का description या तो क्लास के डॉकस्ट्रिंग से या Field क्लास के तर्क description से लिया जाता है।
  • स्कीमा को कुंजी के रूप में उपनामों का उपयोग करके डिफ़ॉल्ट रूप से उत्पन्न किया जाता है, लेकिन इसे model_json_schema() या [model_dump_json()][pydantic.main.BaseModel पर कॉल करके मॉडल प्रॉपर्टी नामों का उपयोग करके उत्पन्न किया जा सकता है। model_dump_json] by_alias=False कीवर्ड तर्क के साथ।

本文总阅读量