-
-
Notifications
You must be signed in to change notification settings - Fork 426
Description
Describe the bug
A clear and concise description of what the bug is.
I upgraded datamodel-codegen recently from 0.35 to 0.52.2 and now there seems to be some weird behavior for how the default is specified for a field of type list[NestedClass]. If you specify the default as [] in graphql definition, the default is set as default_factory=lambda:NestedClass.model_validate([]).
I was able to pinpoint the version that introduced this change to be 0.44
To Reproduce
Example schema: (tmp.graphql)
input TagInput {
name: String!
value: String!
}
input ModelInput {
tags: [TagInput!] = []
}
Used commandline:
$ datamodel-codegen --input tmp.graphql --input-file-type graphql --output tmp.py --output-model-type pydantic_v2.BaseModel
The resulting output:
# generated by datamodel-codegen:
# filename: tmp.graphql
# timestamp: 2026-01-06T20:05:20+00:00
from __future__ import annotations
from typing import Literal
from pydantic import BaseModel, Field
from typing_extensions import TypeAliasType
Boolean = TypeAliasType("Boolean", bool)
"""
The `Boolean` scalar type represents `true` or `false`.
"""
String = TypeAliasType("String", str)
"""
The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.
"""
class TagInput(BaseModel):
name: String
value: String
typename__: Literal['TagInput'] | None = Field('TagInput', alias='__typename')
class ModelInput(BaseModel):
tags: list[TagInput] | None = Field(
default_factory=lambda: TagInput.model_validate([])
)
typename__: Literal['ModelInput'] | None = Field('ModelInput', alias='__typename')Expected behavior
I would expect the field default factory to be something like this:
class ModelInput(GraphQLModel):
tags: Annotated[
list[TagInput] | None,
Field(default_factory=list),
]However, the result is instead:
class ModelInput(BaseModel):
tags: list[TagInput] | None = Field(
default_factory=lambda: TagInput.model_validate([])
)This results in a failure if we try to instantiate this class without specifying this field (which should be allowed).
In [1]: from scripts.tmp import ModelInput
In [2]: ModelInput()
---------------------------------------------------------------------------
ValidationError Traceback (most recent call last)
Cell In[2], line 1
----> 1 ModelInput()
[... skipping hidden 1 frame]
File tmp.py:32, in ModelInput.<lambda>()
30 class ModelInput(BaseModel):
31 tags: list[TagInput] | None = Field(
---> 32 default_factory=lambda: TagInput.model_validate([])
33 )
34 typename__: Literal['ModelInput'] | None = Field('ModelInput', alias='__typename')
File .venv/lib/python3.13/site-packages/pydantic/main.py:716, in BaseModel.model_validate(cls, obj, strict, extra, from_attributes, context, by_alias, by_name)
710 if by_alias is False and by_name is not True:
711 raise PydanticUserError(
712 'At least one of `by_alias` or `by_name` must be set to True.',
713 code='validate-by-alias-and-name-false',
714 )
--> 716 return cls.__pydantic_validator__.validate_python(
717 obj,
718 strict=strict,
719 extra=extra,
720 from_attributes=from_attributes,
721 context=context,
722 by_alias=by_alias,
723 by_name=by_name,
724 )
ValidationError: 1 validation error for TagInput
Input should be a valid dictionary or instance of TagInput [type=model_type, input_value=[], input_type=list]
For further information visit https://errors.pydantic.dev/2.12/v/model_type
Version:
- OS: MacOS 15.6
- Python version: 3.11
- datamodel-code-generator version: introduced in 0.44 (but still in as of 0.52.2)
Additional context
For context, the reproducible snippet that I provided is part of a larger definition from a sister team I work with which is generated from a c# implementation + hot chocolate library.