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

Cannot override the template `pydantic_v2/ConfigDict.jinja2`

Open nbro10 opened this issue 1 year ago • 1 comments

Describe the bug

So, I was trying to override this ConfigDict.jinja2 template and I placed it at templates/pydantic_v2/ConfigDict.jinja2, but use_enum_values=True is not being added to the ConfigDict of the generated models

To Reproduce

Use this template at templates/pydantic_v2/ConfigDict.jinja2

model_config = ConfigDict(
{%- for field_name, value in config.dict(exclude_unset=True).items() %}
    {{ field_name }}={{ value }},
{%- endfor %}
    use_enum_values=True,
)

Used commandline:

datamodel-codegen --input test.json --output test.py --use-union-operator --field-constraints  --input-file-type=jsonschema --use-standard-collections --use-double-quotes --use-schema-description --capitalise-enum-members --custom-template-dir templates --use-title-as-name  --output-model-type=pydantic_v2.BaseModel --extra-template-data templates/data.json

Now, --extra-template-data templates/data.json actually does not help too because only a few fields are supported. This PR https://github.com/koxudaxi/datamodel-code-generator/pull/2134 I created should allow us to specify use_enum_values=True in a file like templates/data.json

{
    "#all#": {
        "config": {
            "use_enum_values": true
        }
    }
}

I know that it's reading from the templates folder, which I specify, because I can override the Enum.jinja2, which I placed at templates/Enum.jinja2

Where is the bug

I think the problem is that BaseModel.jinja2 includes ConfigDict.jinja2 using just the name {% include 'ConfigDict.jinja2' %}, so it just includes the one in the same directory, not ours.

{% for decorator in decorators -%}
{{ decorator }}
{% endfor -%}
class {{ class_name }}({{ base_class }}):{% if comment is defined %}  # {{ comment }}{% endif %}
{%- if description %}
    """
    {{ description | indent(4) }}
    """
{%- endif %}
{%- if not fields and not description %}
    pass
{%- endif %}
{%- if config %}
{%- filter indent(4) %}
{% include 'ConfigDict.jinja2' %}
{%- endfilter %}
{%- endif %}
{%- for field in fields -%}
    {%- if not field.annotated and field.field %}
    {{ field.name }}: {{ field.type_hint }} = {{ field.field }}
    {%- else %}
    {%- if field.annotated %}
    {{ field.name }}: {{ field.annotated }}
    {%- else %}
    {{ field.name }}: {{ field.type_hint }}
    {%- endif %}
    {%- if not (field.required or (field.represented_default == 'None' and field.strip_default_none)) or field.data_type.is_optional
            %} = {{ field.represented_default }}
    {%- endif -%}
    {%- endif %}
    {%- if field.docstring %}
    """
    {{ field.docstring | indent(4) }}
    """
    {%- endif %}
{%- for method in methods -%}
    {{ method }}
{%- endfor -%}
{%- endfor -%}

Possible solution

Right now, the solution would be to override BaseModel.jinja2 to define our ConfigDict class.

Still, I'd expect to be able to override also ConfigDict.jinja2.

Expected behavior

My template overrides the original one.

Version:

  • OS: Mac OS Sonoma
  • Python version: Python 3.11.9
  • datamodel-code-generator version: 0.26.2

nbro10 avatar Oct 25 '24 14:10 nbro10

I just hit something similar. I was overriding BaseModel, and it tried to open ConfigDict.jinja2 in the same directory, not falling back to the one that ships with DCG. I'm going to guess this would require overloading the Jinja loader and doing some custom logic to first look in the override dir, then fall back to the default dir.

jkugler avatar May 20 '25 23:05 jkugler