Lewati ke isi

JSON Schema

??? api "Dokumentasi API" pydantic.json_schema

Pydantic memungkinkan pembuatan dan penyesuaian skema JSON secara otomatis dari model. Skema JSON yang dihasilkan mematuhi spesifikasi berikut:

Menghasilkan Skema JSON

Gunakan fungsi berikut untuk menghasilkan skema JSON:

!!! catatan Metode ini berbeda dengan BaseModel.model_dump_json dan TypeAdapter.dump_json, yang masing-masing membuat serial instance dari model atau tipe yang diadaptasi . Metode ini mengembalikan string JSON. Sebagai perbandingan, BaseModel.model_json_schema dan TypeAdapter.json_schema masing-masing mengembalikan dict jsonable yang mewakili skema JSON dari model atau tipe yang diadaptasi.

!!! catatan "pada sifat skema JSON "jsonable"" Mengenai sifat "jsonable" dari hasil model_json_schema, panggil json.dumps(m.model_json_schema()) pada beberapa BaseModel m mengembalikan string JSON yang valid. Demikian pula, untuk TypeAdapter.json_schema, memanggil json.dumps(TypeAdapter(<some_type>).json_schema()) mengembalikan string JSON yang valid.

!!! tip Pydantic menawarkan dukungan untuk keduanya:

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.

Berikut ini contoh pembuatan skema JSON dari BaseModel :

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"
    }
  ]
}
"""

Mengonfigurasi JsonSchemaMode

Tentukan mode pembuatan skema JSON melalui parameter mode dalam metode model_json_schema dan TypeAdapter.json_schema. Secara default, mode disetel ke 'validation' , yang menghasilkan skema JSON yang sesuai dengan skema validasi model.

JsonSchemaMode adalah alias tipe yang mewakili opsi yang tersedia untuk parameter mode :

  • 'validation'
  • 'serialization'

Berikut ini contoh cara menentukan parameter mode , dan pengaruhnya terhadap skema JSON yang dihasilkan:

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',
}
"""

Menyesuaikan Skema JSON

Skema JSON yang dihasilkan dapat dikustomisasi pada tingkat bidang dan tingkat model melalui:

  1. Kustomisasi tingkat bidang dengan konstruktor Field.
  2. Kustomisasi tingkat model dengan model_config

Baik di tingkat bidang maupun model, Anda dapat menggunakan opsi json_schema_extra untuk menambahkan informasi tambahan ke skema JSON. Bagian Menggunakan json_schema_extra di bawah memberikan detail lebih lanjut tentang opsi ini.

Untuk tipe khusus, Pydantic menawarkan alat lain untuk menyesuaikan pembuatan skema JSON:

  1. WithJsonSchema
  2. Lewati anotasi SkipJsonSchema
  3. Menerapkan __get_pydantic_core_schema__
  4. Menerapkan __get_pydantic_json_schema__

Kustomisasi Tingkat Lapangan

Secara opsional, fungsi Field dapat digunakan untuk memberikan informasi tambahan tentang bidang dan validasi.

Beberapa parameter bidang digunakan secara eksklusif untuk menyesuaikan Skema JSON yang dihasilkan:

  • title : Judul bidang.
  • description : Deskripsi lapangan.
  • examples : Contoh lapangan.
  • json_schema_extra : Properti Skema JSON tambahan yang akan ditambahkan ke bidang.
  • field_title_generator : Fungsi yang secara terprogram menetapkan judul bidang, berdasarkan nama dan informasinya.

Berikut ini contohnya:

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"
}
"""

Kendala Field yang Tidak Ditegakkan

Jika Pydantic menemukan batasan yang tidak diterapkan, kesalahan akan muncul. Jika Anda ingin memaksa batasan untuk muncul dalam skema, meskipun tidak diperiksa saat penguraian, Anda dapat menggunakan argumen variadik ke Field dengan nama atribut skema mentah:

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',
}
"""

Anda dapat menentukan modifikasi skema JSON melalui konstruktor Field melalui typing.Annotated juga:

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"
}
"""

Pembuatan judul bidang terprogram

Parameter field_title_generator dapat digunakan untuk menghasilkan judul bidang secara terprogram berdasarkan nama dan infonya.

Lihat contoh berikut:

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"
}
"""

Kustomisasi Tingkat Model

Anda juga dapat menggunakan model config untuk menyesuaikan pembuatan skema JSON pada suatu model. Secara khusus, opsi konfigurasi berikut ini relevan:

Menggunakan json_schema_extra

Opsi json_schema_extra dapat digunakan untuk menambahkan informasi tambahan ke skema JSON, baik di tingkat Bidang atau di tingkat Model . Anda dapat meneruskan dict atau Callable ke json_schema_extra .

Menggunakan json_schema_extra dengan dict

Anda dapat meneruskan dict ke json_schema_extra untuk menambahkan informasi tambahan ke skema JSON:

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"
}
"""

Menggunakan json_schema_extra dengan Callable

Anda dapat meneruskan Callable ke json_schema_extra untuk mengubah skema JSON dengan fungsi:

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"
}
"""

Menggabungkan json_schema_extra

Mulai v2.9, Pydantic menggabungkan kamus json_schema_extra dari tipe beranotasi. Pola ini menawarkan pendekatan yang lebih aditif terhadap penggabungan dibandingkan perilaku penggantian sebelumnya. Ini bisa sangat membantu untuk kasus penggunaan kembali informasi tambahan skema json di berbagai jenis.

Kami melihat perubahan ini sebagian besar sebagai perbaikan bug, karena perubahan ini menyelesaikan perbedaan yang tidak disengaja dalam perilaku penggabungan json_schema_extra antara instance BaseModel dan TypeAdapter - lihat masalah ini untuk detail selengkapnya.

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"
}
"""

Jika Anda lebih memilih spesifikasi json_schema_extra terakhir untuk menggantikan spesifikasi sebelumnya, Anda dapat menggunakan callable untuk membuat perubahan yang lebih signifikan, termasuk menambahkan atau menghapus kunci, atau mengubah nilai. Anda dapat menggunakan pola ini jika Anda ingin meniru perilaku penggantian json_schema_extra yang ada di Pydantic v2.8 dan sebelumnya:

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

??? api "Dokumentasi API" [pydantic.json_schema.WithJsonSchema][pydantic.json_schema.DenganJsonSchema]

!!! tip Menggunakan WithJsonSchema] lebih disukai daripada menerapkan __get_pydantic_json_schema__ untuk tipe khusus, karena lebih sederhana dan tidak rawan kesalahan.

Anotasi WithJsonSchema dapat digunakan untuk mengganti skema JSON (dasar) yang dihasilkan untuk tipe tertentu tanpa perlu mengimplementasikan __get_pydantic_core_schema__ atau __get_pydantic_json_schema__ pada tipe itu sendiri.

Ini memberikan cara untuk menyetel skema JSON untuk tipe yang sebaliknya akan menimbulkan kesalahan saat membuat skema JSON, seperti Callable , atau tipe yang memiliki skema inti is-instance .

Misalnya, penggunaan [PlainValidator][pydantic.function_validators.PlainValidator] dalam contoh berikut akan menimbulkan kesalahan saat membuat skema JSON karena [PlainValidator][pydantic.function_validators.PlainValidator] adalah Callable . Namun, dengan menggunakan anotasi WithJsonSchema, kita dapat mengganti skema JSON yang dihasilkan untuk tipe MyInt kustom:

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"
}
"""

!!! catatan Seperti yang dibahas dalam masalah ini , di masa mendatang, kemungkinan Pydantic akan menambahkan dukungan bawaan untuk pembuatan skema JSON untuk tipe seperti [PlainValidator][pydantic.function_validators.PlainValidator], tetapi anotasi WithJsonSchema akan tetap berguna untuk tipe khusus lainnya.

Lewati anotasi SkipJsonSchema

??? api "Dokumentasi API" pydantic.json_schema.SkipJsonSchema

Anotasi SkipJsonSchema dapat digunakan untuk melewati bidang penyertaan (atau bagian dari spesifikasi bidang) dari skema JSON yang dihasilkan. Lihat dokumen API untuk detail selengkapnya.

Menerapkan __get_pydantic_core_schema__

Tipe khusus (digunakan sebagai field_name: TheType atau field_name: Annotated[TheType, ...] ) serta metadata Annotated (digunakan sebagai field_name: Annotated[int, SomeMetadata] ) dapat mengubah atau mengganti skema yang dihasilkan dengan menerapkan __get_pydantic_core_schema__ . Metode ini menerima dua argumen posisi:

  1. Anotasi tipe yang sesuai dengan tipe ini (jadi dalam kasus TheType[T][int] akan menjadi TheType[int] ).
  2. Penangan/panggilan balik untuk memanggil pelaksana __get_pydantic_core_schema__ berikutnya.

Sistem handler bekerja seperti mode='wrap' validators . Dalam hal ini masukannya adalah tipe dan keluarannya adalah core_schema .

Berikut adalah contoh tipe khusus yang menggantikan core_schema yang dihasilkan:

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'}

Karena Pydantic tidak akan tahu cara membuat skema untuk CompressedString , jika Anda memanggil handler(source) dalam metode __get_pydantic_core_schema__ Anda akan mendapatkan pydantic.errors.PydanticSchemaGenerationError kesalahan. Ini akan menjadi kasus untuk sebagian besar tipe kustom, jadi Anda hampir tidak pernah ingin memanggil handler untuk tipe kustom.

Proses untuk metadata Annotated hampir sama kecuali bahwa Anda biasanya dapat memanggil handler agar Pydantic menangani pembuatan skema.

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]
    """

Sejauh ini kita telah membungkus skemanya, tetapi jika Anda hanya ingin memodifikasinya atau mengabaikannya , Anda juga bisa.

Untuk mengubah skema, panggil dulu handlernya, lalu mutasikan hasilnya:

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]
    """

!!! tip Perhatikan bahwa Anda harus mengembalikan skema, meskipun Anda baru saja memutasikannya pada tempatnya.

Untuk mengganti skema sepenuhnya, jangan panggil pengendali dan kembalikan CoreSchema Anda sendiri :

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]
    """

Seperti yang terlihat di atas, memberi anotasi pada bidang dengan tipe BaseModel dapat digunakan untuk mengubah atau mengganti skema json yang dihasilkan. Namun, jika Anda ingin memanfaatkan penyimpanan metadata melalui Annotated , namun tidak ingin mengganti skema JSON yang dihasilkan, Anda dapat menggunakan pendekatan berikut dengan versi tanpa operasi __get_pydantic_core_schema__ yang diimplementasikan pada kelas metadata:

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)],
    )
}
"""

Menerapkan __get_pydantic_json_schema__

Anda juga dapat menerapkan __get_pydantic_json_schema__ untuk mengubah atau mengganti skema json yang dihasilkan. Memodifikasi metode ini hanya memengaruhi skema JSON - tidak memengaruhi skema inti, yang digunakan untuk validasi dan serialisasi.

Berikut ini contoh modifikasi skema JSON yang dihasilkan:

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"
}
"""

Menggunakan field_title_generator

Parameter field_title_generator dapat digunakan untuk menghasilkan judul bidang secara terprogram berdasarkan nama dan infonya. Ini mirip dengan field_title_generator tingkat bidang, tetapi opsi ConfigDict akan diterapkan ke semua bidang kelas.

Lihat contoh berikut:

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"
}
"""

Menggunakan model_title_generator

Opsi konfigurasi model_title_generator mirip dengan opsi field_title_generator , tetapi ini berlaku untuk judul model itu sendiri, dan menerima kelas model sebagai input.

Lihat contoh berikut:

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"
}
"""

Jenis skema JSON

Jenis, jenis bidang khusus, dan batasan (seperti max_length ) dipetakan ke format spesifikasi yang sesuai dalam urutan prioritas berikut (bila tersedia yang setara):

  1. Inti Skema JSON
  2. Validasi Skema JSON
  3. Tipe Data OpenAPI
  4. Bidang JSON format standar digunakan untuk menentukan ekstensi Pydantic untuk subtipe string yang lebih kompleks.

Pemetaan skema bidang dari skema Python atau Pydantic ke JSON dilakukan sebagai berikut:

Pembuatan skema tingkat atas

Anda juga dapat membuat skema JSON tingkat atas yang hanya menyertakan daftar model dan terkait sub-model dalam ` $defs`: ```py output="json" import json from pydantic import BaseModel from pydantic.json_schema import models_json_schema class Foo(BaseModel): a: str = Tidak ada class Model(BaseModel): b: Bilah kelas Foo(BaseModel): c: int _, top_level_schema = models_json_schema( [(Model, 'validasi'), (Bilah, 'validasi')], title='Skema Saya' ) print(json.dumps(top_level_schema, indent=2)) """ { "$ defs": { "Batang": { "properti": { "C": { "judul": "C", "ketik": "bilangan bulat" } }, "diperlukan": [ "C" ], "judul": "Bilah", "tipe": "objek" }, "Bodoh": { "properti": { "A": { "default": batal, "judul": "A", "ketik": "string" } }, "judul": "Bodoh", "tipe": "objek" }, "Model": { "properti": { "B": { " $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": { "$ def/Foo" } }, "diperlukan": [ "B" ], "judul": "Model", "tipe": "objek" } }, "title": "Skema Saya" } """

## 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',
}
"""

Di bawah ini adalah pendekatan yang dapat Anda gunakan untuk mengecualikan bidang apa pun dari skema yang tidak memiliki skema json yang valid:

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',
}
"""

Menyesuaikan $ref`s in JSON Schema The format of `$ref s dapat diubah dengan memanggil model_json_schema()

atau model_dump_json() dengan argumen kata kunci ref_template . Definisi selalu disimpan di bawah kunci $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 Atribut JSON dan direferensikan, sesuai spesifikasi.

  • Sub-model dengan modifikasi (melalui kelas Field ) seperti judul kustom, deskripsi, atau nilai default, disertakan secara rekursif, bukan direferensikan.
  • description model diambil dari docstring kelas atau description argumen ke kelas Field .
  • Skema dibuat secara default menggunakan alias sebagai kunci, namun dapat dibuat menggunakan nama properti model dengan memanggil model_json_schema() atau [model_dump_json()][pydantic.main.BaseModel. model_dump_json] dengan argumen kata kunci by_alias=False .

本文总阅读量