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 with Literal with multiple values results in single Literal value #1878

Closed
ldej opened this issue Mar 7, 2024 · 0 comments · Fixed by #1885
Closed

Discriminated Union with Literal with multiple values results in single Literal value #1878

ldej opened this issue Mar 7, 2024 · 0 comments · Fixed by #1885

Comments

@ldej
Copy link
Contributor

ldej commented Mar 7, 2024

Describe the bug

Generating models from OpenAPI spec with discriminator being an enum results in Literal with a single value.

To Reproduce

I am using the Pydantic example for discriminated unions: https://docs.pydantic.dev/latest/concepts/unions/#discriminated-unions

class Cat(BaseModel):
    pet_type: Literal["cat"]
    meows: int


class Dog(BaseModel):
    pet_type: Literal["dog"]
    barks: float


class Lizard(BaseModel):
    pet_type: Literal["reptile", "lizard"]  # <- Has 2 values
    scales: bool


class Model(BaseModel):
    pet: Union[Cat, Dog, Lizard] = Field(..., discriminator="pet_type")
    n: int

This results in the following openapi schema. Note that Lizard gets an enum as pet_type:

{
  "openapi": "3.1.0",
  "components": {
    "schemas": {
      "Cat": {
        "properties": {
          "pet_type": {
            "const": "cat",
            "title": "Pet Type"
          },
          "meows": {
            "title": "Meows",
            "type": "integer"
          }
        },
        "required": [
          "pet_type",
          "meows"
        ],
        "title": "Cat",
        "type": "object"
      },
      "Dog": {
        "properties": {
          "pet_type": {
            "const": "dog",
            "title": "Pet Type"
          },
          "barks": {
            "title": "Barks",
            "type": "number"
          }
        },
        "required": [
          "pet_type",
          "barks"
        ],
        "title": "Dog",
        "type": "object"
      },
      "Lizard": {
        "properties": {
          "pet_type": {
            "enum": [
              "reptile",
              "lizard"
            ],
            "title": "Pet Type",
            "type": "string"
          },
          "scales": {
            "title": "Scales",
            "type": "boolean"
          }
        },
        "required": [
          "pet_type",
          "scales"
        ],
        "title": "Lizard",
        "type": "object"
      },
      "Animal": {
        "properties": {
          "pet": {
            "discriminator": {
              "mapping": {
                "cat": "#/components/schemas/Cat",
                "dog": "#/components/schemas/Dog",
                "lizard": "#/components/schemas/Lizard",
                "reptile": "#/components/schemas/Lizard"
              },
              "propertyName": "pet_type"
            },
            "oneOf": [
              {
                "$ref": "#/components/schemas/Cat"
              },
              {
                "$ref": "#/components/schemas/Dog"
              },
              {
                "$ref": "#/components/schemas/Lizard"
              }
            ],
            "title": "Pet"
          },
          "n": {
            "title": "N",
            "type": "integer"
          }
        }
      }
    }
  },
  "paths": {},
  "info": {}
}

Used commandline:

$ datamodel-codegen --input test-openapi.json --input-file-type openapi --use-subclass-enum --output models.py

Output:

from __future__ import annotations

from enum import Enum
from typing import Optional, Union

from pydantic import BaseModel, Field
from typing_extensions import Literal


class Cat(BaseModel):
    pet_type: Literal['cat'] = Field(..., const=True, title='Pet Type')
    meows: int = Field(..., title='Meows')


class Dog(BaseModel):
    pet_type: Literal['dog'] = Field(..., const=True, title='Pet Type')
    barks: float = Field(..., title='Barks')


class PetType(str, Enum):  # <- Unused Enum
    reptile = 'reptile'
    lizard = 'lizard'


class Lizard(BaseModel):
    pet_type: Literal['reptile'] = Field(..., title='Pet Type')  # <- Has 1 value
    scales: bool = Field(..., title='Scales')


class Animal(BaseModel):
    pet: Optional[Union[Cat, Dog, Lizard]] = Field(
        None, discriminator='pet_type', title='Pet'
    )
    n: Optional[int] = Field(None, title='N')

Note that the Enum PetType is not used and note that Lizard has pet_type: Literal['reptile']

I have tried --enum-field-as-literal all which removed the PetType, but Lizard still has a single value for pet_type.

Expected behavior
I expected that Lizard would contain both values "reptile", "lizard" or PetType.reptile, PetType.lizard

Version:

  • OS: Ubuntu 22.04.3 LTS
  • Python version: 3.10.12
  • datamodel-code-generator version: 0.25.4

Additional context
Add any other context about the problem here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
1 participant