Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/datamodel_code_generator/model/pydantic/base_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,23 @@ def _get_strict_field_constraint_value(self, constraint: str, value: Any) -> Any
return value
return int(value)

def _get_default_as_pydantic_model(self) -> str | None:
def _get_default_as_pydantic_model(self) -> str | None: # noqa: PLR0911, PLR0912
if isinstance(self.default, WrappedDefault):
return f"lambda :{self.default!r}"
# Handle the case where self.data_type.is_list is True directly (e.g., GraphQL)
if self.data_type.is_list and len(self.data_type.data_types) == 1:
data_type_child = self.data_type.data_types[0]
if (
data_type_child.reference
and isinstance(data_type_child.reference.source, BaseModelBase)
and isinstance(self.default, list)
):
if not self.default:
return STANDARD_LIST
return ( # pragma: no cover
f"lambda :[{data_type_child.alias or data_type_child.reference.source.class_name}."
f"{self._PARSE_METHOD}(v) for v in {self.default!r}]"
)
for data_type in self.data_type.data_types or (self.data_type,):
# TODO: Check nested data_types
if data_type.is_dict:
Expand Down
32 changes: 32 additions & 0 deletions tests/data/expected/main/graphql/empty_list_default.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# generated by datamodel-codegen:
# filename: empty_list_default.graphql
# timestamp: 2019-07-26T00:00:00+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=list)
typename__: Literal['ModelInput'] | None = Field('ModelInput', alias='__typename')
8 changes: 8 additions & 0 deletions tests/data/graphql/empty_list_default.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
input TagInput {
name: String!
value: String!
}

input ModelInput {
tags: [TagInput!] = []
}
19 changes: 19 additions & 0 deletions tests/main/graphql/test_main_graphql.py
Original file line number Diff line number Diff line change
Expand Up @@ -890,3 +890,22 @@ def test_main_graphql_no_typename(output_file: Path) -> None:
expected_file="no_typename.py",
extra_args=["--graphql-no-typename"],
)


def test_main_graphql_empty_list_default(output_file: Path) -> None:
"""Test that empty list default generates default_factory=list, not model_validate([]).

When a GraphQL field has a list type with an empty list default (e.g., tags: [TagInput!] = []),
the generator should produce `Field(default_factory=list)` instead of
`Field(default_factory=lambda: TagInput.model_validate([]))`.

This test verifies fix for issue #2947.
"""
run_main_and_assert(
input_path=GRAPHQL_DATA_PATH / "empty_list_default.graphql",
output_path=output_file,
input_file_type="graphql",
assert_func=assert_file_content,
expected_file="empty_list_default.py",
extra_args=["--output-model-type", "pydantic_v2.BaseModel"],
)
Loading