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

Fix regression in core schema generation for indirect definition references #8702

Merged
merged 3 commits into from Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion pydantic/_internal/_core_utils.py
Expand Up @@ -228,7 +228,7 @@ def _handle_ser_schemas(self, ser_schema: core_schema.SerSchema, f: Walk) -> cor
def handle_definitions_schema(self, schema: core_schema.DefinitionsSchema, f: Walk) -> core_schema.CoreSchema:
new_definitions: list[core_schema.CoreSchema] = []
for definition in schema['definitions']:
if 'schema_ref' and 'ref' in definition:
if 'schema_ref' in definition and 'ref' in definition:
# This indicates a purposely indirect reference
# We want to keep such references around for implications related to JSON schema, etc.:
new_definitions.append(definition)
Expand Down
87 changes: 87 additions & 0 deletions tests/test_discriminated_union.py
Expand Up @@ -1763,3 +1763,90 @@ class CreateObjectDto(BaseModel):
'type': 'object',
},
}


def test_nested_discriminator() -> None:
"""
The exact details of the JSON schema produced are not necessarily important; the test was added in response to a
regression that caused the inner union was losing its discriminator. Even if the schema changes, the important
dmontagu marked this conversation as resolved.
Show resolved Hide resolved
thing is that the core schema (and therefore JSON schema) produced has an actual discriminated union in it.
"""
dmontagu marked this conversation as resolved.
Show resolved Hide resolved

class Step_A(BaseModel):
type: Literal['stepA']
count: int

class Step_B(BaseModel):
type: Literal['stepB']
value: float

class MyModel(BaseModel):
type: Literal['mixed']
sub_models: list['SubModel']
dmontagu marked this conversation as resolved.
Show resolved Hide resolved
steps: Union[Step_A, Step_B] = Field(
default=None,
discriminator='type',
)

class SubModel(MyModel):
type: Literal['mixed']
blending: float

MyModel.model_rebuild()
assert MyModel.model_json_schema() == {
'$defs': {
'Step_A': {
'properties': {
'count': {'title': 'Count', 'type': 'integer'},
'type': {'const': 'stepA', 'title': 'Type'},
},
'required': ['type', 'count'],
'title': 'Step_A',
'type': 'object',
},
'Step_B': {
'properties': {
'type': {'const': 'stepB', 'title': 'Type'},
'value': {'title': 'Value', 'type': 'number'},
},
'required': ['type', 'value'],
'title': 'Step_B',
'type': 'object',
},
'SubModel': {
'properties': {
'blending': {'title': 'Blending', 'type': 'number'},
'steps': {
'default': None,
'discriminator': {
'mapping': {'stepA': '#/$defs/Step_A', 'stepB': '#/$defs/Step_B'},
'propertyName': 'type',
},
'oneOf': [{'$ref': '#/$defs/Step_A'}, {'$ref': '#/$defs/Step_B'}],
'title': 'Steps',
},
'sub_models': {'items': {'$ref': '#/$defs/SubModel'}, 'title': 'Sub Models', 'type': 'array'},
'type': {'const': 'mixed', 'title': 'Type'},
},
'required': ['type', 'sub_models', 'blending'],
'title': 'SubModel',
'type': 'object',
},
},
'properties': {
'steps': {
'default': None,
'discriminator': {
'mapping': {'stepA': '#/$defs/Step_A', 'stepB': '#/$defs/Step_B'},
'propertyName': 'type',
},
'oneOf': [{'$ref': '#/$defs/Step_A'}, {'$ref': '#/$defs/Step_B'}],
'title': 'Steps',
},
'sub_models': {'items': {'$ref': '#/$defs/SubModel'}, 'title': 'Sub Models', 'type': 'array'},
'type': {'const': 'mixed', 'title': 'Type'},
},
'required': ['type', 'sub_models'],
'title': 'MyModel',
'type': 'object',
}