Skip to content

Commit 88c7fe4

Browse files
authored
Fix required list fields ignoring empty default values (#2958)
* Fix required list fields ignoring empty default values * Add parameterized test for pydantic v1/v2 default value handling * Fix incorrect default value in test schema for FamilyPets
1 parent 4cbf3bf commit 88c7fe4

5 files changed

Lines changed: 74 additions & 8 deletions

File tree

src/datamodel_code_generator/model/pydantic/base_model.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ def __str__(self) -> str: # noqa: PLR0912
220220
elif isinstance(discriminator, dict): # pragma: no cover
221221
data["discriminator"] = discriminator["propertyName"]
222222

223-
if self.required:
223+
if self.required and not self.has_default:
224224
default_factory = None
225225
elif self.default is not UNDEFINED and self.default is not None and "default_factory" not in data:
226226
default_factory = self._get_default_as_pydantic_model()
@@ -249,7 +249,7 @@ def __str__(self) -> str: # noqa: PLR0912
249249

250250
if self.use_annotated:
251251
field_arguments = self._process_annotated_field_arguments(field_arguments)
252-
elif self.required:
252+
elif self.required and not default_factory:
253253
field_arguments = ["...", *field_arguments]
254254
elif not default_factory:
255255
default_repr = repr_set_sorted(self.default) if isinstance(self.default, set) else repr(self.default)

tests/data/expected/main/jsonschema/has_default_value.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,17 @@ class Pet(BaseModel):
2525

2626

2727
class Family(BaseModel):
28-
__root__: list[ID] = ['abc', 'efg']
28+
__root__: list[ID] = Field(
29+
default_factory=lambda: [ID.parse_obj(v) for v in ['abc', 'efg']]
30+
)
2931

3032

3133
class FamilyPets(BaseModel):
32-
__root__: list[Pet] = ['taro', 'shiro']
34+
__root__: list[Pet] = Field(
35+
default_factory=lambda: [
36+
Pet.parse_obj(v) for v in [{'name': 'taro'}, {'name': 'shiro'}]
37+
]
38+
)
3339

3440

3541
class Person(BaseModel):
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# generated by datamodel-codegen:
2+
# filename: has_default_value.json
3+
# timestamp: 2019-07-26T00:00:00+00:00
4+
5+
from __future__ import annotations
6+
7+
from enum import Enum
8+
9+
from pydantic import BaseModel, Field, RootModel
10+
11+
12+
class TeamType(Enum):
13+
Department = 'Department'
14+
Division = 'Division'
15+
BusinessUnit = 'BusinessUnit'
16+
Organization = 'Organization'
17+
18+
19+
class ID(RootModel[str]):
20+
root: str = 'abc'
21+
22+
23+
class Pet(BaseModel):
24+
name: str | None = None
25+
26+
27+
class Family(RootModel[list[ID]]):
28+
root: list[ID] = Field(
29+
default_factory=lambda: [ID.model_validate(v) for v in ['abc', 'efg']]
30+
)
31+
32+
33+
class FamilyPets(RootModel[list[Pet]]):
34+
root: list[Pet] = Field(
35+
default_factory=lambda: [
36+
Pet.model_validate(v) for v in [{'name': 'taro'}, {'name': 'shiro'}]
37+
]
38+
)
39+
40+
41+
class Person(BaseModel):
42+
id: ID | None = Field(default_factory=lambda: ID('abc'))
43+
user: Pet | None = None
44+
firstName: str | None = Field(None, description="The person's first name.")
45+
team: TeamType | None = 'Department'
46+
anotherTeam: TeamType | None = 'Department'
47+
Family_1: Family | None = Field(None, alias='Family')
48+
FamilyPets_1: FamilyPets | None = Field(None, alias='FamilyPets')

tests/data/jsonschema/has_default_value.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@
4545
"$ref": "#/definitions/Pet"
4646
},
4747
"default": [
48-
"taro",
49-
"shiro"
48+
{"name": "taro"},
49+
{"name": "shiro"}
5050
]
5151
}
5252
},

tests/main/jsonschema/test_main_jsonschema.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3254,14 +3254,26 @@ def test_jsonschema_use_title_as_name_nested_titles_pydantic(output_file: Path)
32543254
)
32553255

32563256

3257-
def test_main_jsonschema_has_default_value(output_file: Path) -> None:
3257+
@pytest.mark.parametrize(
3258+
("output_model", "expected_file"),
3259+
[
3260+
("pydantic.BaseModel", "has_default_value.py"),
3261+
pytest.param(
3262+
"pydantic_v2.BaseModel",
3263+
"has_default_value_pydantic_v2.py",
3264+
marks=PYDANTIC_V2_SKIP,
3265+
),
3266+
],
3267+
)
3268+
def test_main_jsonschema_has_default_value(output_model: str, expected_file: str, output_file: Path) -> None:
32583269
"""Test default value handling."""
32593270
run_main_and_assert(
32603271
input_path=JSON_SCHEMA_DATA_PATH / "has_default_value.json",
32613272
output_path=output_file,
32623273
input_file_type="jsonschema",
32633274
assert_func=assert_file_content,
3264-
expected_file="has_default_value.py",
3275+
expected_file=expected_file,
3276+
extra_args=["--output-model-type", output_model],
32653277
)
32663278

32673279

0 commit comments

Comments
 (0)