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

Discriminated union schemas use oneOf instead of anyOf #4335

Merged
merged 1 commit into from Aug 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions changes/4335-MaxwellPayne.md
@@ -0,0 +1 @@
Discriminated union models now use `oneOf` instead of `anyOf` when generating OpenAPI schema definitions.
11 changes: 6 additions & 5 deletions pydantic/schema.py
Expand Up @@ -705,7 +705,8 @@ def field_singleton_sub_fields_schema(
else:
s: Dict[str, Any] = {}
# https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#discriminator-object
if field.discriminator_key is not None:
field_has_discriminator: bool = field.discriminator_key is not None
if field_has_discriminator:
assert field.sub_fields_mapping is not None

discriminator_models_refs: Dict[str, Union[str, Dict[str, Any]]] = {}
Expand Down Expand Up @@ -748,16 +749,16 @@ def field_singleton_sub_fields_schema(
definitions.update(sub_definitions)
if schema_overrides and 'allOf' in sub_schema:
# if the sub_field is a referenced schema we only need the referenced
# object. Otherwise we will end up with several allOf inside anyOf.
# object. Otherwise we will end up with several allOf inside anyOf/oneOf.
# See https://github.com/pydantic/pydantic/issues/1209
sub_schema = sub_schema['allOf'][0]

if sub_schema.keys() == {'discriminator', 'anyOf'}:
# we don't want discriminator information inside anyOf choices, this is dealt with elsewhere
if sub_schema.keys() == {'discriminator', 'oneOf'}:
# we don't want discriminator information inside oneOf choices, this is dealt with elsewhere
sub_schema.pop('discriminator')
sub_field_schemas.append(sub_schema)
nested_models.update(sub_nested_models)
s['anyOf'] = sub_field_schemas
s['oneOf' if field_has_discriminator else 'anyOf'] = sub_field_schemas
return s, definitions, nested_models


Expand Down
4 changes: 2 additions & 2 deletions tests/test_dataclasses.py
Expand Up @@ -1190,7 +1190,7 @@ class Users(BaseModel):
}


def test_discrimated_union_basemodel_instance_value():
def test_discriminated_union_basemodel_instance_value():
@pydantic.dataclasses.dataclass
class A:
l: Literal['a']
Expand All @@ -1212,7 +1212,7 @@ class Top:
'sub': {
'title': 'Sub',
'discriminator': {'propertyName': 'l', 'mapping': {'a': '#/definitions/A', 'b': '#/definitions/B'}},
'anyOf': [{'$ref': '#/definitions/A'}, {'$ref': '#/definitions/B'}],
'oneOf': [{'$ref': '#/definitions/A'}, {'$ref': '#/definitions/B'}],
}
},
'required': ['sub'],
Expand Down
2 changes: 1 addition & 1 deletion tests/test_forward_ref.py
Expand Up @@ -588,7 +588,7 @@ class Dog(BaseModel):
assert module.Pet.schema() == {
'title': 'Pet',
'discriminator': {'propertyName': 'type', 'mapping': {'cat': '#/definitions/Cat', 'dog': '#/definitions/Dog'}},
'anyOf': [{'$ref': '#/definitions/Cat'}, {'$ref': '#/definitions/Dog'}],
'oneOf': [{'$ref': '#/definitions/Cat'}, {'$ref': '#/definitions/Dog'}],
'definitions': {
'Cat': {
'title': 'Cat',
Expand Down
16 changes: 8 additions & 8 deletions tests/test_schema.py
Expand Up @@ -2675,7 +2675,7 @@ class Model(BaseModel):
'lizard': '#/definitions/Lizard',
},
},
'anyOf': [
'oneOf': [
{'$ref': '#/definitions/Cat'},
{'$ref': '#/definitions/Dog'},
{'$ref': '#/definitions/Lizard'},
Expand Down Expand Up @@ -2708,7 +2708,7 @@ class Model(BaseModel):
'propertyName': 'color',
'mapping': {'black': '#/definitions/BlackCat', 'white': '#/definitions/WhiteCat'},
},
'anyOf': [{'$ref': '#/definitions/BlackCat'}, {'$ref': '#/definitions/WhiteCat'}],
'oneOf': [{'$ref': '#/definitions/BlackCat'}, {'$ref': '#/definitions/WhiteCat'}],
},
'Dog': {
'title': 'Dog',
Expand Down Expand Up @@ -2775,11 +2775,11 @@ class Model(BaseModel):
'dog': '#/definitions/Dog',
},
},
'anyOf': [
'oneOf': [
{
'anyOf': [
'oneOf': [
{
'anyOf': [
'oneOf': [
{'$ref': '#/definitions/BlackCatWithHeight'},
{'$ref': '#/definitions/BlackCatWithWeight'},
]
Expand Down Expand Up @@ -2858,7 +2858,7 @@ class Model(BaseModel):
'properties': {
'number': {'title': 'Number', 'type': 'integer'},
'pet': {
'anyOf': [{'$ref': '#/definitions/Cat'}, {'$ref': '#/definitions/Dog'}],
'oneOf': [{'$ref': '#/definitions/Cat'}, {'$ref': '#/definitions/Dog'}],
'discriminator': {
'mapping': {'cat': '#/definitions/Cat', 'dog': '#/definitions/Dog'},
'propertyName': 'typeOfPet',
Expand Down Expand Up @@ -2960,9 +2960,9 @@ class Model(BaseModel):
'dog': '#/definitions/Dog',
},
},
'anyOf': [
'oneOf': [
{
'anyOf': [
'oneOf': [
{'$ref': '#/definitions/BlackCat'},
{'$ref': '#/definitions/WhiteCat'},
],
Expand Down