datamodel-code-generator icon indicating copy to clipboard operation
datamodel-code-generator copied to clipboard

Default value should be from type RootModel and not string

Open ubaumann opened this issue 1 year ago • 0 comments

Describe the bug When I use --use-annotated, the default value for a RootModel field is a string. I would expect it to be the RootModel("value")

To Reproduce

Example schema:

{
    "$defs": {
        "AdminStateLeaf": {
            "$ref": "#/$defs/AdminStateType",
            "title": "AdminStateLeaf"
        },
        "AdminStateType": {
            "$ref": "#/$defs/EnumerationEnum",
            "title": "AdminStateType"
        },
        "EnumerationEnum": {
            "enum": [
                "enable",
                "disable"
            ],
            "title": "EnumerationEnum",
            "type": "string"
        }
    },
    "description": "List of configured device interfaces",
    "properties": {
        "interfaces:admin-state": {
            "$ref": "#/$defs/AdminStateLeaf",
            "default": "enable"
        }
    },
    "title": "InterfacesListEntry",
    "type": "object"
}

Used commandline:

$ datamodel-codegen --input-file-type jsonschema --input simple.json --output-model-type pydantic_v2.BaseModel --output model_simple.py --use-annotated

Expected behavior I expect the default value to be the RootModel. Expected model (difference is in InterfacesListEntry):

from __future__ import annotations

from enum import Enum
from typing import Optional

from pydantic import BaseModel, Field, RootModel
from typing_extensions import Annotated


class EnumerationEnum(Enum):
    enable = 'enable'
    disable = 'disable'


class AdminStateType(RootModel[EnumerationEnum]):
    root: Annotated[EnumerationEnum, Field(title='AdminStateType')]


class AdminStateLeaf(RootModel[AdminStateType]):
    root: Annotated[AdminStateType, Field(title='AdminStateLeaf')]


class InterfacesListEntry(BaseModel):
    interfaces_admin_state: Annotated[
        AdminStateLeaf, Field(AdminStateLeaf("enable"), alias="interfaces:admin-state")
    ]

Generated model:

from __future__ import annotations

from enum import Enum
from typing import Optional

from pydantic import BaseModel, Field, RootModel
from typing_extensions import Annotated


class EnumerationEnum(Enum):
    enable = 'enable'
    disable = 'disable'


class AdminStateType(RootModel[EnumerationEnum]):
    root: Annotated[EnumerationEnum, Field(title='AdminStateType')]


class AdminStateLeaf(RootModel[AdminStateType]):
    root: Annotated[AdminStateType, Field(title='AdminStateLeaf')]


class InterfacesListEntry(BaseModel):
    interfaces_admin_state: Annotated[
        Optional[AdminStateLeaf], Field(alias='interfaces:admin-state')
    ] = 'enable'

Version:

  • OS: Ubunut
  • Python version: 3.10.14
  • datamodel-code-generator version: develop branch

Additional context The following are examples of the generated model (in the first part, the value is a string and not a RootModel):

>>> import model_simple
>>> model_simple.InterfacesListEntry.parse_obj({})
InterfacesListEntry(interfaces_admin_state='enable')
>>> model_simple.InterfacesListEntry.parse_obj({"interfaces:admin-state":"disable"})
InterfacesListEntry(interfaces_admin_state=AdminStateLeaf(root=AdminStateType(root=<EnumerationEnum.disable: 'disable'>)))
>>> model_simple.InterfacesListEntry.parse_obj({"interfaces:admin-state":"enable"})
InterfacesListEntry(interfaces_admin_state=AdminStateLeaf(root=AdminStateType(root=<EnumerationEnum.enable: 'enable'>)))
>>> 

This is the same test using the expected code.

>>> import model_simple
>>> model_simple.InterfacesListEntry.parse_obj({})
InterfacesListEntry(interfaces_admin_state=AdminStateLeaf(root=AdminStateType(root=<EnumerationEnum.enable: 'enable'>)))
>>> model_simple.InterfacesListEntry.parse_obj({"interfaces:admin-state":"disable"})
InterfacesListEntry(interfaces_admin_state=AdminStateLeaf(root=AdminStateType(root=<EnumerationEnum.disable: 'disable'>)))

ubaumann avatar Oct 15 '24 19:10 ubaumann