JSON Schema
??? api "API-documentatie" pydantic.json_schema
Pydantic maakt het automatisch maken en aanpassen van JSON-schema's op basis van modellen mogelijk. De gegenereerde JSON-schema's voldoen aan de volgende specificaties:
JSON-schema genereren¶
Gebruik de volgende functies om JSON-schema te genereren:
BaseModel.model_json_schema
retourneert een jsonable dictaat van het schema van een model.TypeAdapter.json_schema
retourneert een jsonable dictaat van het schema van een aangepast type.
!!! opmerking Deze methoden moeten niet worden verward met BaseModel.model_dump_json
en TypeAdapter.dump_json
, die respectievelijk instanties van het model of het aangepaste type serialiseren . Deze methoden retourneren JSON-tekenreeksen. Ter vergelijking: BaseModel.model_json_schema
en TypeAdapter.json_schema
retourneren een jsonable dictaat dat respectievelijk het JSON-schema van het model of het aangepaste type vertegenwoordigt.
!!! opmerking "over de "jsonable" aard van JSON-schema" Met betrekking tot de "jsonable" aard van de model_json_schema
resultaten, aanroepen json.dumps(m.model_json_schema())
op sommige BaseModel
retourneert m
een geldige JSON-tekenreeks. Op dezelfde manier geldt voor TypeAdapter.json_schema
het aanroepen van json.dumps(TypeAdapter(<some_type>).json_schema())
retourneert een geldige JSON-tekenreeks.
!!! tip Pydantic biedt ondersteuning voor zowel:
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.
Hier is een voorbeeld van het genereren van een JSON-schema op basis van een 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"
}
]
}
"""
De JsonSchemaMode
configureren¶
Geef de modus voor het genereren van JSON-schema's op via de mode
in de methoden model_json_schema
en TypeAdapter.json_schema
. Standaard is de modus ingesteld op 'validation'
, wat een JSON-schema produceert dat overeenkomt met het validatieschema van het model.
De JsonSchemaMode
is een typealias die de beschikbare opties voor de mode
vertegenwoordigt:
'validation'
'serialization'
Hier is een voorbeeld van hoe u de mode
parameter opgeeft en hoe deze het gegenereerde JSON-schema beïnvloedt:
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-schema aanpassen¶
Het gegenereerde JSON-schema kan zowel op veldniveau als op modelniveau worden aangepast via:
- Aanpassing op veldniveau met de constructor
Field
. - Aanpassing op modelniveau met
model_config
Op zowel veld- als modelniveau kunt u de optie json_schema_extra
gebruiken om extra informatie aan het JSON-schema toe te voegen. In het gedeelte json_schema_extra
gebruiken hieronder vindt u meer informatie over deze optie.
Voor aangepaste typen biedt Pydantic andere tools voor het aanpassen van het genereren van JSON-schema's:
WithJsonSchema
annotatieSkipJsonSchema
annotatie- Implementatie van
__get_pydantic_core_schema__
- Implementatie van
__get_pydantic_json_schema__
Aanpassing op veldniveau¶
Optioneel kan de functie Field
worden gebruikt om extra informatie over het veld en validaties te geven.
Sommige veldparameters worden uitsluitend gebruikt om het gegenereerde JSON-schema aan te passen:
title
: De titel van het veld.description
: De beschrijving van het veld.examples
: De voorbeelden van het veld.json_schema_extra
: Extra JSON Schema-eigenschappen die aan het veld moeten worden toegevoegd.field_title_generator
: Een functie die programmatisch de titel van het veld instelt, op basis van de naam en info.
Hier is een voorbeeld:
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"
}
"""
Niet-afgedwongen Field
¶
Als Pydantic beperkingen constateert die niet worden afgedwongen, zal er een fout optreden. Als u wilt dat de beperking in het schema verschijnt, ook al wordt deze niet gecontroleerd bij het parseren, kunt u variadische argumenten gebruiken voor Field
met de onbewerkte schemakenmerknaam:
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',
}
"""
U kunt JSON-schemawijzigingen ook opgeven via de constructor Field
via typing.Annotated
:
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"
}
"""
Genereren van programmatische veldtitels¶
De parameter field_title_generator
kan worden gebruikt om programmatisch de titel voor een veld te genereren op basis van de naam en info.
Zie het volgende voorbeeld:
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"
}
"""
Aanpassing op modelniveau¶
U kunt ook model config gebruiken om het genereren van JSON-schema's voor een model aan te passen. Concreet zijn de volgende configuratieopties relevant:
title
json_schema_extra
schema_generator
json_schema_mode_override
field_title_generator
model_title_generator
json_schema_extra
gebruiken¶
De optie json_schema_extra
kan worden gebruikt om extra informatie aan het JSON-schema toe te voegen, op veldniveau of op modelniveau . U kunt een dict
of een Callable
doorgeven aan json_schema_extra
.
json_schema_extra
gebruiken met een dict
¶
U kunt een dict
doorgeven aan json_schema_extra
om extra informatie aan het JSON-schema toe te voegen:
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"
}
"""
json_schema_extra
gebruiken met een Callable
¶
U kunt een Callable
doorgeven aan json_schema_extra
om het JSON-schema te wijzigen met een functie:
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
samenvoegen¶
Vanaf v2.9 voegt Pydantic json_schema_extra
woordenboeken van geannoteerde typen samen. Dit patroon biedt een meer additieve benadering van samenvoegen dan het eerdere overschrijfgedrag. Dit kan behoorlijk handig zijn als er sprake is van hergebruik van extra JSON-schema-informatie voor meerdere typen.
We hebben deze wijziging grotendeels gezien als een bugfix, omdat hiermee onbedoelde verschillen in het samenvoeggedrag json_schema_extra
tussen BaseModel
en TypeAdapter
instanties worden opgelost. Zie dit probleem voor meer details.
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"
}
"""
Als u er de voorkeur aan geeft dat de laatste van uw json_schema_extra
-specificaties de vorige overschrijft, kunt u een callable
gebruiken om belangrijkere wijzigingen aan te brengen, waaronder het toevoegen of verwijderen van sleutels, of het wijzigen van waarden. U kunt dit patroon gebruiken als u het gedrag van de json_schema_extra
overschrijvingen in Pydantic v2.8 en eerder wilt nabootsen:
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
annotatie¶
??? api "API-documentatie" pydantic.json_schema.WithJsonSchema
!!! tip Het gebruik van WithJsonSchema
] heeft de voorkeur boven het implementeren van __get_pydantic_json_schema__
voor aangepaste typen, omdat het eenvoudiger is en minder foutgevoelig.
De annotatie WithJsonSchema
kan worden gebruikt om het gegenereerde (basis) JSON-schema voor een bepaald type te overschrijven zonder de noodzaak om __get_pydantic_core_schema__
of __get_pydantic_json_schema__
op het type zelf te implementeren.
Dit biedt een manier om een JSON-schema in te stellen voor typen die anders fouten zouden veroorzaken bij het produceren van een JSON-schema, zoals Callable
, of typen die een kernschema is-instance
hebben.
Het gebruik van een PlainValidator
in het volgende voorbeeld zou anders een fout opleveren bij het produceren van een JSON-schema, omdat de PlainValidator
een Callable
is. Door echter de annotatie WithJsonSchema
te gebruiken, kunnen we het gegenereerde JSON-schema voor het aangepaste MyInt
type overschrijven:
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"
}
"""
!!! note Zoals besproken in dit nummer is het waarschijnlijk dat Pydantic in de toekomst ingebouwde ondersteuning zal toevoegen voor het genereren van JSON-schema's voor typen als PlainValidator
, maar de annotatie WithJsonSchema
zal nog steeds nuttig zijn voor andere aangepaste typen.
SkipJsonSchema
annotatie¶
??? api "API-documentatie" pydantic.json_schema.SkipJsonSchema
De annotatie SkipJsonSchema
kan worden gebruikt om een inclusief veld (of een deel van de specificaties van een veld) uit het gegenereerde JSON-schema over te slaan. Zie de API-documentatie voor meer details.
Implementatie van __get_pydantic_core_schema__
¶
Aangepaste typen (gebruikt als field_name: TheType
of field_name: Annotated[TheType, ...]
) en Annotated
metadata (gebruikt als field_name: Annotated[int, SomeMetadata]
) kan het gegenereerde schema wijzigen of overschrijven door __get_pydantic_core_schema__
te implementeren. Deze methode ontvangt twee positionele argumenten:
- De typeannotatie die overeenkomt met dit type (dus in het geval van
TheType[T][int]
zou ditTheType[int]
zijn). - Een handler/callback om de volgende implementator van
__get_pydantic_core_schema__
aan te roepen.
Het handlersysteem werkt net als mode='wrap'
validators . In dit geval is de invoer het type en de uitvoer een core_schema
.
Hier is een voorbeeld van een aangepast type dat het gegenereerde core_schema
overschrijft :
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'}
Omdat Pydantic niet zou weten hoe hij een schema voor CompressedString
moest genereren, zou je, als je handler(source)
aanroept in zijn __get_pydantic_core_schema__
methode, een pydantic.errors.PydanticSchemaGenerationError
fout. Dit zal het geval zijn voor de meeste aangepaste typen, dus u wilt bijna nooit handler
inschakelen voor aangepaste typen.
Het proces voor Annotated
metagegevens is vrijwel hetzelfde, behalve dat u over het algemeen handler
kunt aanroepen om Pydantic het schema te laten genereren.
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]
"""
Tot nu toe hebben we het schema ingepakt, maar als je het alleen maar wilt wijzigen of negeren , kun je dat ook doen.
Om het schema te wijzigen, roept u eerst de handler aan en muteert u vervolgens het resultaat:
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 Houd er rekening mee dat u een schema moet retourneren, zelfs als u het alleen op zijn plaats muteert.
Om het schema volledig te overschrijven, roept u de handler niet aan en retourneert u uw eigen 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]
"""
Zoals hierboven te zien is, kan het annoteren van een veld met een BaseModel
type worden gebruikt om het gegenereerde JSON-schema te wijzigen of te overschrijven. Als u echter wilt profiteren van het opslaan van metagegevens via Annotated
, maar u het gegenereerde JSON-schema niet wilt overschrijven, kunt u de volgende aanpak gebruiken met een no-op-versie van __get_pydantic_core_schema__
geïmplementeerd in de metadataklasse:
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)],
)
}
"""
Implementatie van __get_pydantic_json_schema__
¶
U kunt ook __get_pydantic_json_schema__
implementeren om het gegenereerde json-schema te wijzigen of te overschrijven. Het wijzigen van deze methode heeft alleen invloed op het JSON-schema. Het heeft geen invloed op het kernschema, dat wordt gebruikt voor validatie en serialisatie.
Hier is een voorbeeld van het wijzigen van het gegenereerde JSON-schema:
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
gebruiken¶
De parameter field_title_generator
kan worden gebruikt om programmatisch de titel voor een veld te genereren op basis van de naam en info. Dit is vergelijkbaar met het veldniveau field_title_generator
, maar de optie ConfigDict
wordt toegepast op alle velden van de klasse.
Zie het volgende voorbeeld:
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
gebruiken¶
De configuratieoptie model_title_generator
is vergelijkbaar met de optie field_title_generator
, maar is van toepassing op de titel van het model zelf en accepteert de modelklasse als invoer.
Zie het volgende voorbeeld:
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-schematypen¶
Typen, aangepaste veldtypen en beperkingen (zoals max_length
) worden in de volgende prioriteitsvolgorde toegewezen aan de overeenkomstige specificatie-indelingen (als er een equivalent beschikbaar is):
- JSON-schemakern
- JSON-schemavalidatie
- OpenAPI-gegevenstypen
- Het JSON-veld
format
wordt gebruikt om Pydantic-extensies te definiëren voor complexerestring
.
De veldschematoewijzing van Python of Pydantic naar JSON-schema gebeurt als volgt:
Schema genereren op het hoogste niveau¶
U kunt ook een JSON-schema op het hoogste niveau genereren dat alleen een lijst met modellen en gerelateerde modellen bevat submodellen in zijn ` $defs`: ```py output="json" importeer json van pydantic importeer BaseModel van pydantic.json_schema import models_json_schema klasse Foo(BaseModel): a: str = Geen klasse Model(BaseModel): b: Foo class Bar(BaseModel): c: int _, top_level_schema = models_json_schema( [(Model, 'validatie'), (Bar, 'validatie')], title='Mijn schema' ) print(json.dumps(top_level_schema, indent=2)) """ { "$ defs": { "Bar": { "eigenschappen": { "C": { "titel": "C", "type": "geheel getal" } }, "vereist": [ "C" ], "title": "Bar", "type": "object" }, "Foe": { "eigenschappen": { "A": { "standaard": nul, "titel": "A", "type": "tekenreeks" } }, "title": "Foe", "type": "object" }, "Model": { "eigenschappen": { "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": { "$ defs/Foo" } }, "vereist": [ "B" ], "titel": "Model", "type": "object" } }, "title": "Mijn schema" } """
## 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',
}
"""
Hieronder vindt u een aanpak die u kunt gebruiken om velden uit het schema uit te sluiten die geen geldige json-schema's hebben:
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',
}
"""
Het aanpassen van de $ref`s in JSON Schema The format of `$ref
s kunnen worden gewijzigd door model_json_schema()
aan te roepen¶
of model_dump_json()
met het trefwoordargument ref_template
. De definities worden altijd onder de sleutel opgeslagen $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-kenmerk en waarnaar wordt verwezen, volgens de specificatie.
- Submodellen met wijzigingen (via de klasse
Field
), zoals een aangepaste titel, beschrijving of standaardwaarde, worden recursief opgenomen in plaats van dat er naar wordt verwezen. - De
description
voor modellen wordt overgenomen uit de docstring van de klasse of uit dedescription
naar de klasseField
. - Het schema wordt standaard gegenereerd met aliassen als sleutels, maar kan in plaats daarvan worden gegenereerd met namen van modeleigenschappen door
model_json_schema()
of [model_dump_json()
][pydantic.main.BaseModel aan te roepen. model_dump_json] met hetby_alias=False
trefwoordargument.
本文总阅读量次