koxudaxi / datamodel-code-generator

Pydantic model and dataclasses.dataclass generator for easy conversion of JSON, OpenAPI, JSON Schema, and YAML data sources.
https://koxudaxi.github.io/datamodel-code-generator/
MIT License
2.64k stars 294 forks source link

output-model-type `pydantic_2.BaseModel` with `Field()` generates invalid pydantic v2 classes #1534

Open luminoso opened 1 year ago

luminoso commented 1 year ago

Describe the bug

Generating "pydantic_v2.BaseModel with Field() generates an impossible solution for current pydantic v2 implementation.

To Reproduce

Example schema:

{
  "$schema": "http://json-schema.org/draft-07/schema",
  "properties": {
    "Settings": {
      "title": "having a title will force a Field()",
      "properties": {
        "name": {
          "type": "string"
        }
      },
      "required": ["name"],
      "type": "object"
    }
  },
  "required": ["Settings"],
  "type": "object"
}

Used commandline:

$ datamodel-codegen --input-file-type jsonschema --output-model-type "pydantic_v2.BaseModel" --input sample.json --output generated_classes.py

Expected behavior The generated class should work when running with pydantic v2.

Version:

Additional context The generate class is the following:

from __future__ import annotations

from pydantic import BaseModel, Field

class Settings(BaseModel):
    name: str

class Model(BaseModel):
    Settings: Settings = Field(
        ..., title='having a title will force field defined with Field()'
    )

Which is exactly the same as the one generated with --output-model-type "pydantic.BaseModel". However, due to pydantic v2 limitations, the generated class will lead to a pydantic v2 error: RecursionError: maximum recursion depth exceeded

The pydantic v1 and v2 behaviour duality has been reported at pydantic repository: https://github.com/pydantic/pydantic/issues/7327

To make datamodel-code-generator work with the current pydantic v2 implementation, as suggested by https://github.com/pydantic/pydantic/issues/7327#issuecomment-1706012608, generated classes must have a different name from the field names, like so:

from __future__ import annotations

from pydantic import BaseModel, Field

class Settings_(BaseModel):
    name: str

class Model(BaseModel):
    Settings: Settings_ = Field(
        ..., title='having a title will force field defined with Field()'
    )

In which, having a class name that doesn't collide with field name, makes the generated class to work with pydantic v2.

koxudaxi commented 11 months ago

Thank you for creating the issue. I didn't know the behavior. I will fix it.

I may change the field name instead of the Model name.

Ps7ch3 commented 11 months ago

🤔 Any updates ?

tcrapts-ictu commented 8 months ago

I had the same issue. I solved it by using the --use-annotated flag. However, I still it would be helpful if the root cause is addressed.