Skip to content

Commit 24b576e

Browse files
authored
Fix missing Field import with multiple aliases on required fields (#2992)
1 parent b58970a commit 24b576e

8 files changed

Lines changed: 95 additions & 4 deletions

File tree

src/datamodel_code_generator/model/pydantic/base_model.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -297,10 +297,6 @@ def annotated(self) -> str | None:
297297
@property
298298
def imports(self) -> tuple[Import, ...]:
299299
"""Get all required imports including Field if needed."""
300-
# Fast path: skip expensive self.field check for simple required fields
301-
if self.required and not self.nullable and not self.alias and self.constraints is None and not self.extras:
302-
return super().imports
303-
304300
if self.field:
305301
return chain_as_tuple(super().imports, (IMPORT_FIELD,))
306302
return super().imports
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": ["full_name", "customer_name"],
3+
"email": ["email_address", "mail"]
4+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"Customer": {
3+
"full_name": "John"
4+
}
5+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# generated by datamodel-codegen:
2+
# filename: <dict>
3+
# timestamp: 2019-07-26T00:00:00+00:00
4+
5+
from __future__ import annotations
6+
7+
from pydantic import AliasChoices, BaseModel, Field
8+
9+
10+
class Customer(BaseModel):
11+
full_name: str = Field(
12+
..., validation_alias=AliasChoices('name', 'full_name', 'customer_name')
13+
)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# generated by datamodel-codegen:
2+
# filename: multiple_aliases_required.json
3+
# timestamp: 2019-07-26T00:00:00+00:00
4+
5+
from __future__ import annotations
6+
7+
from pydantic import AliasChoices, BaseModel, Field
8+
9+
10+
class Root(BaseModel):
11+
full_name: str = Field(
12+
..., validation_alias=AliasChoices('name', 'full_name', 'customer_name')
13+
)
14+
email_address: str = Field(
15+
..., validation_alias=AliasChoices('email', 'email_address', 'mail')
16+
)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"title": "Root",
4+
"type": "object",
5+
"properties": {
6+
"name": { "type": "string" },
7+
"email": { "type": "string" }
8+
},
9+
"required": ["name", "email"]
10+
}

tests/main/jsonschema/test_main_jsonschema.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8495,3 +8495,19 @@ def test_main_jsonschema_dynamic_ref_in_defs_pydantic_v2(output_file: Path) -> N
84958495
expected_file="dynamic_ref_in_defs_pydantic_v2.py",
84968496
extra_args=["--output-model-type", "pydantic_v2.BaseModel"],
84978497
)
8498+
8499+
8500+
def test_main_jsonschema_multiple_aliases_required_pydantic_v2(output_file: Path) -> None:
8501+
"""Test multiple aliases with AliasChoices on required fields for Pydantic v2. (#2989)."""
8502+
run_main_and_assert(
8503+
input_path=JSON_SCHEMA_DATA_PATH / "multiple_aliases_required.json",
8504+
output_path=output_file,
8505+
input_file_type="jsonschema",
8506+
assert_func=assert_file_content,
8507+
extra_args=[
8508+
"--aliases",
8509+
str(ALIASES_DATA_PATH / "multiple_aliases_required.json"),
8510+
"--output-model-type",
8511+
"pydantic_v2.BaseModel",
8512+
],
8513+
)

tests/main/test_dynamic_models.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,37 @@ def test_execute_multi_module_no_models() -> None:
459459
assert models == {}
460460

461461

462+
def test_multiple_aliases_required_field() -> None:
463+
"""Test dynamic models with multiple aliases on required fields. (#2989)."""
464+
schema = make_object_schema({"name": {"type": "string"}}, required=["name"])
465+
config = GenerateConfig(
466+
input_file_type=InputFileType.JsonSchema,
467+
output_model_type=DataModelType.PydanticV2BaseModel,
468+
aliases={"name": ["full_name", "customer_name"]},
469+
class_name="Customer",
470+
)
471+
assert_dynamic_models(
472+
schema,
473+
{"Customer": {"name": "John"}},
474+
EXPECTED_PATH / "multiple_aliases_required.json",
475+
config=config,
476+
)
477+
478+
479+
def test_multiple_aliases_required_field_code_output() -> None:
480+
"""Test generated code includes Field import with multiple aliases on required fields. (#2989)."""
481+
schema = make_object_schema({"name": {"type": "string"}}, required=["name"])
482+
config = GenerateConfig(
483+
input_file_type=InputFileType.JsonSchema,
484+
output_model_type=DataModelType.PydanticV2BaseModel,
485+
aliases={"name": ["full_name", "customer_name"]},
486+
class_name="Customer",
487+
)
488+
result = generate(input_=schema, config=config)
489+
assert isinstance(result, str)
490+
assert_output(result, EXPECTED_PATH / "multiple_aliases_required_code.py")
491+
492+
462493
def test_execute_multi_module_enum_only() -> None:
463494
"""Test _execute_multi_module with enum only to cover non-BaseModel branch."""
464495
from datamodel_code_generator.dynamic import _execute_multi_module

0 commit comments

Comments
 (0)