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

"--field-constraints" produces extra "RootModel"s that don't get collapsed by "--collapse-root-models"

Open xyqyear opened this issue 1 year ago • 0 comments

Describe the bug

Sometimes "--collapse-root-models" works without "--field-constraints" but fails when using it.

I found this when converting docker compose's spec to pydantic v2 model. I've condensed the problematic part as a schema.

To Reproduce

Example schema:

{
    "properties": {
        "service": {
            "id": "#/definitions/service",
            "type": "object",
            "properties": {
                "annotations": {
                    "$ref": "#/definitions/list_or_dict"
                }
            }
        }
    },
    "additionalProperties": false,
    "definitions": {
        "list_or_dict": {
            "oneOf": [
                {
                    "type": "object",
                    "patternProperties": {
                        ".+": {
                            "type": [
                                "string",
                                "number",
                                "boolean",
                                "null"
                            ]
                        }
                    }
                },
                {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                }
            ]
        }
    }
}

Used commandline:

datamodel-codegen --input test.json --output-model-type pydantic_v2.BaseModel --field-constraints --collapse-root-models --output test.py

I get:

from __future__ import annotations

from typing import Dict, List, Optional, Union

from pydantic import BaseModel, ConfigDict, RootModel


class ListOrDict1(RootModel[List[str]]):
    root: List[str]


class Service(BaseModel):
    annotations: Optional[
        Union[Dict[str, Optional[Union[str, float, bool]]], ListOrDict1]
    ] = None


class Model(BaseModel):
    model_config = ConfigDict(
        extra="forbid",
    )
    service: Optional[Service] = None

I expect RootModel be collapsed when using "--collapse-root-models".

When I use this command: (without --field-constraints, but with constr)

datamodel-codegen --input test.json --output-model-type pydantic_v2.BaseModel --collapse-root-models --output test.py

It produces the following result:

from __future__ import annotations

from typing import Dict, List, Optional, Union

from pydantic import BaseModel, ConfigDict, constr


class Service(BaseModel):
    annotations: Optional[
        Union[Dict[constr(pattern=r'.+'), Optional[Union[str, float, bool]]], List[str]]
    ] = None


class Model(BaseModel):
    model_config = ConfigDict(
        extra='forbid',
    )
    service: Optional[Service] = None

As you can see, there is no more RootModel. However this is not optimal because pyright doesn't like constr(...) being a type expression.

Version:

  • OS: Debian 12
  • Python version: 3.12.7
  • datamodel-code-generator version: 0.26.2

xyqyear avatar Oct 17 '24 17:10 xyqyear