diff --git a/changes/4335-MaxwellPayne.md b/changes/4335-MaxwellPayne.md new file mode 100644 index 0000000000..5edafff49d --- /dev/null +++ b/changes/4335-MaxwellPayne.md @@ -0,0 +1 @@ +Discriminated union models now use `oneOf` instead of `anyOf` when generating OpenAPI schema definitions. diff --git a/pydantic/schema.py b/pydantic/schema.py index 593cab5ad5..1909356e8a 100644 --- a/pydantic/schema.py +++ b/pydantic/schema.py @@ -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]]] = {} @@ -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 diff --git a/tests/test_dataclasses.py b/tests/test_dataclasses.py index 56ed9bf398..93b6886ff9 100644 --- a/tests/test_dataclasses.py +++ b/tests/test_dataclasses.py @@ -1201,7 +1201,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'] @@ -1223,7 +1223,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'], diff --git a/tests/test_forward_ref.py b/tests/test_forward_ref.py index 04eac879de..2ad1dc53a1 100644 --- a/tests/test_forward_ref.py +++ b/tests/test_forward_ref.py @@ -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', diff --git a/tests/test_schema.py b/tests/test_schema.py index 1bfe146b4a..6a9a636e60 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -2675,7 +2675,7 @@ class Model(BaseModel): 'lizard': '#/definitions/Lizard', }, }, - 'anyOf': [ + 'oneOf': [ {'$ref': '#/definitions/Cat'}, {'$ref': '#/definitions/Dog'}, {'$ref': '#/definitions/Lizard'}, @@ -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', @@ -2775,11 +2775,11 @@ class Model(BaseModel): 'dog': '#/definitions/Dog', }, }, - 'anyOf': [ + 'oneOf': [ { - 'anyOf': [ + 'oneOf': [ { - 'anyOf': [ + 'oneOf': [ {'$ref': '#/definitions/BlackCatWithHeight'}, {'$ref': '#/definitions/BlackCatWithWeight'}, ] @@ -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', @@ -2960,9 +2960,9 @@ class Model(BaseModel): 'dog': '#/definitions/Dog', }, }, - 'anyOf': [ + 'oneOf': [ { - 'anyOf': [ + 'oneOf': [ {'$ref': '#/definitions/BlackCat'}, {'$ref': '#/definitions/WhiteCat'}, ],