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

Can't set custom title for enum field #4188

Closed
lovervoorde opened this issue Jun 27, 2022 · 4 comments
Closed

Can't set custom title for enum field #4188

lovervoorde opened this issue Jun 27, 2022 · 4 comments
Labels
bug V1 Bug related to Pydantic V1.X

Comments

@lovervoorde
Copy link

lovervoorde commented Jun 27, 2022

Checks

  • [y] I added a descriptive title to this issue
  • [y] I have searched (google, github) for similar issues and couldn't find anything
  • [y] I have read and followed the docs and still think this is a bug

Bug

Output of python -c "import pydantic.utils; print(pydantic.utils.version_info())":

             pydantic version: 1.7.4
            pydantic compiled: True
                 install path: /usr/local/lib/python3.8/site-packages/pydantic
               python version: 3.8.11 (default, Aug 17 2021, 03:04:29)  [GCC 8.3.0]
                     platform: Linux-5.13.0-1019-aws-x86_64-with-glibc2.2.5
     optional deps. installed: ['typing-extensions']

I have an enum field, color_choice. I would like the schema to include the title 'Color Choice' (with a space).

First attempt:

import json
from enum import Enum

from pydantic import BaseModel


class ColorChoice(str, Enum):
    RED = "RED"
    BLUE = "BLUE"


class Shape(BaseModel):
    color_choice: ColorChoice


print(json.dumps(json.loads(Shape.schema_json()), indent=4))

# Result:
# {
#     "title": "Shape",
#     "type": "object",
#     "properties": {
#         "color_choice": {
#             "$ref": "#/definitions/ColorChoice"
#         }
#     },
#     "required": [
#         "color_choice"
#     ],
#     "definitions": {
#         "ColorChoice": {
#             "title": "ColorChoice",
#             "description": "An enumeration.",
#             "enum": [
#                 "RED",
#                 "BLUE"
#             ],
#             "type": "string"
#         }
#     }
# }

If I try setting the title using Field, I get an extra allOf, and the title without the space is still included in the ref.

import json
from enum import Enum

from pydantic import BaseModel, Field


class ColorChoice(str, Enum):
    RED = "RED"
    BLUE = "BLUE"


class Shape(BaseModel):
    color_choice: ColorChoice = Field(
        title="Color Choice",
    )


print(json.dumps(json.loads(Shape.schema_json()), indent=4))

# Result:
# {
#     "title": "Shape",
#     "type": "object",
#     "properties": {
#         "color_choice": {
#             "title": "Color Choice",
#             "allOf": [
#                 {
#                     "$ref": "#/definitions/ColorChoice"
#                 }
#             ]
#         }
#     },
#     "required": [
#         "color_choice"
#     ],
#     "definitions": {
#         "ColorChoice": {
#             "title": "ColorChoice",
#             "description": "An enumeration.",
#             "enum": [
#                 "RED",
#                 "BLUE"
#             ],
#             "type": "string"
#         }
#     }
# }

I am hoping to just get:

# {
#     "title": "Shape",
#     "type": "object",
#     "properties": {
#         "color_choice": {
#             "$ref": "#/definitions/ColorChoice"
#         }
#     },
#     "required": [
#         "color_choice"
#     ],
#     "definitions": {
#         "ColorChoice": {
#             "title": "Color Choice",
#             "enum": [
#                 "RED",
#                 "BLUE"
#             ],
#             "type": "string"
#         }
#     }
# }

I have found these somewhat similar issues:
#1748
#1749
#2592
#3896

Thanks in advance!

@lovervoorde lovervoorde added the bug V1 Bug related to Pydantic V1.X label Jun 27, 2022
@NotoriousPyro
Copy link

NotoriousPyro commented Jul 11, 2022

I think this is expected behaviour since enums cannot be extended so I think Pydantic works around this by creating a new model including title and allOf which are essentially "an array of items, and those items are all a ref of this enum".

I could be wrong though. I'm trying to set the title for an enum myself, and the solution you suggested (Field(title="hello")) works for what I'm doing.

@m10d
Copy link

m10d commented Jul 17, 2022

I think this is expected behaviour

assuming this is the same issue (which it appears to be, at least at the symptom level), @samuelcolvin seems to be aware of it (#3896 (comment)); I don't believe there have been any updates on a fix, though :(

@IterableTrucks
Copy link

The extra allOf affects the Swagger-UI output. If you add some docstring in the ColorChoice like below:

class ColorChoice(str, Enum):
    """
    some description in detail
    """
    RED = "RED"
    BLUE = "BLUE"

The first case without Field will normally show docstring in the description of color_choice field, but the latter won't.

@dmontagu
Copy link
Contributor

As @IterableTrucks notes, in OpenAPI<3.1 sibling keys next to $ref are ignored, and this use of allOf provides the same semantic meaning in both JSON schema and OpenAPI. So I think we will keep this behavior by default so as to prevent breaking Swagger UI.

However, you can modify the schema for the enum by the use of __modify_schema__ in v1, or __get_pydantic_json_schema__ in v2.

In v1:

import json
from enum import Enum

from pydantic import BaseModel


class ColorChoice(str, Enum):
    RED = "RED"
    BLUE = "BLUE"

    @classmethod
    def __modify_schema__(cls, schema):
        schema['title'] = 'ABC'  # the title will be 'ABC'
        return schema


class Shape(BaseModel):
    color_choice: ColorChoice

This also works in v2, but the __modify_schema__ method is deprecated and should be replaced with __get_pydantic_json_schema__ as shown here:

import json
from enum import Enum

from pydantic import BaseModel
from pydantic.json_schema import GetJsonSchemaHandler
from pydantic_core import core_schema

class ColorChoice(str, Enum):
    RED = "RED"
    BLUE = "BLUE"

    @classmethod
    def __get_pydantic_json_schema__(cls, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler):
        schema = handler(core_schema)
        schema['title'] = 'Abc'  # title will become 'Abc'
        return schema


class Shape(BaseModel):
    color_choice: ColorChoice


print(json.dumps(json.loads(Shape.schema_json()), indent=4))
"""
{
    "type": "object",
    "properties": {
        "color_choice": {
            "$ref": "#/$defs/ColorChoice"
        }
    },
    "required": [
        "color_choice"
    ],
    "title": "Shape",
    "$defs": {
        "ColorChoice": {
            "enum": [
                "RED",
                "BLUE"
            ],
            "title": "Abc",
            "type": "string"
        }
    }
}
"""

We don't do this with = Field(title='Abc') as that is meant to be the way you set the title only for the specific field.

If any of this behavior is a problem, please create a new issue to describe the problem, and we can consider modifying or providing more configurability of this behavior out of the box in v2's GenerateJsonSchema.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug V1 Bug related to Pydantic V1.X
Projects
None yet
Development

No branches or pull requests

5 participants