Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tweak ordering of definitions in generated schemas #8583

Merged
merged 10 commits into from Jan 20, 2024
2 changes: 2 additions & 0 deletions pydantic/_internal/_generate_schema.py
Expand Up @@ -437,6 +437,8 @@ def clean_schema(self, schema: CoreSchema) -> CoreSchema:
if collect_invalid_schemas(schema):
raise self.CollectedInvalid()
schema = validate_core_schema(schema)
if 'definitions' in schema:
schema['definitions'] = list(reversed(schema['definitions']))
return schema

def collect_definitions(self, schema: CoreSchema) -> CoreSchema:
Expand Down
69 changes: 68 additions & 1 deletion tests/test_discriminated_union.py
Expand Up @@ -2,7 +2,7 @@
import sys
from enum import Enum, IntEnum
from types import SimpleNamespace
from typing import Any, Callable, Generic, Optional, Sequence, TypeVar, Union
from typing import Any, Callable, Generic, List, Optional, Sequence, TypeVar, Union

import pytest
from dirty_equals import HasRepr, IsStr
Expand All @@ -12,6 +12,8 @@
from pydantic import BaseModel, ConfigDict, Discriminator, Field, TypeAdapter, ValidationError, field_validator
from pydantic._internal._discriminated_union import apply_discriminator
from pydantic.errors import PydanticUserError
from pydantic.fields import FieldInfo
from pydantic.json_schema import GenerateJsonSchema
from pydantic.types import Tag


Expand Down Expand Up @@ -1696,3 +1698,68 @@ class DiscriminatedModel(BaseModel):
]
except PydanticUserError as exc_info:
assert exc_info.code == 'callable-discriminator-no-tag'


def test_presence_of_discriminator_when_generating_type_adaptor_json_schema_definitions() -> None:
class ItemType(str, Enum):
ITEM1 = 'item1'
ITEM2 = 'item2'

class CreateItem1(BaseModel):
item_type: Annotated[Literal[ItemType.ITEM1], Field(alias='type')]
id: int

class CreateItem2(BaseModel):
item_type: Annotated[Literal[ItemType.ITEM2], Field(alias='type')]
id: int

class CreateObjectDto(BaseModel):
id: int
items: List[
Annotated[
Union[
CreateItem1,
CreateItem2,
],
Field(discriminator='item_type'),
]
]

adaptor = TypeAdapter(
Annotated[CreateObjectDto, FieldInfo(examples=[{'id': 1, 'items': [{'id': 3, 'type': 'ITEM1'}]}])]
StrawHatDrag0n marked this conversation as resolved.
Show resolved Hide resolved
)

schema_map, definitions = GenerateJsonSchema().generate_definitions([(adaptor, 'validation', adaptor.core_schema)])
assert definitions == {
'CreateItem1': {
'properties': {'id': {'title': 'Id', 'type': 'integer'}, 'type': {'const': 'item1', 'title': 'Type'}},
'required': ['type', 'id'],
'title': 'CreateItem1',
'type': 'object',
},
'CreateItem2': {
'properties': {'id': {'title': 'Id', 'type': 'integer'}, 'type': {'const': 'item2', 'title': 'Type'}},
'required': ['type', 'id'],
'title': 'CreateItem2',
'type': 'object',
},
'CreateObjectDto': {
'properties': {
'id': {'title': 'Id', 'type': 'integer'},
'items': {
'items': {
'discriminator': {
'mapping': {'item1': '#/$defs/CreateItem1', 'item2': '#/$defs/CreateItem2'},
'propertyName': 'type',
},
'oneOf': [{'$ref': '#/$defs/CreateItem1'}, {'$ref': '#/$defs/CreateItem2'}],
},
'title': 'Items',
'type': 'array',
},
},
'required': ['id', 'items'],
'title': 'CreateObjectDto',
'type': 'object',
},
}