Skip to content

Commit 3e87a9a

Browse files
authored
Fix root model reuse collapse (#3089)
* Fix root model reuse collapse * Adjust reuse model test target
1 parent 5137795 commit 3e87a9a

4 files changed

Lines changed: 140 additions & 0 deletions

File tree

src/datamodel_code_generator/parser/base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1815,6 +1815,8 @@ def __reuse_model(self, models: list[DataModel], require_update_action_models: l
18151815
model_cache: dict[tuple[HashableComparable, ...], Reference] = {}
18161816
duplicates = []
18171817
for model in models.copy():
1818+
if self.collapse_root_models and isinstance(model, self.data_model_root_type):
1819+
continue
18181820
model_key = model.get_dedup_key()
18191821
cached_model_reference = model_cache.get(model_key)
18201822
if cached_model_reference:
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# generated by datamodel-codegen:
2+
# filename: reuse_model_collapse_root_models_constraints.json
3+
# timestamp: 2019-07-26T00:00:00+00:00
4+
5+
from __future__ import annotations
6+
7+
from pydantic import BaseModel, Field, RootModel
8+
9+
10+
class Shared(BaseModel):
11+
short_code: str = Field(
12+
..., alias='shortCode', max_length=8, min_length=0, title='ShortCode'
13+
)
14+
15+
16+
class RecordA(Shared):
17+
timestamp: str = Field(..., title='Timestamp')
18+
19+
20+
class RecordB(Shared):
21+
created: str = Field(..., title='Timestamp')
22+
label: str = Field(..., title='LongText')
23+
24+
25+
class ReproBugs(RootModel[RecordA | RecordB]):
26+
root: RecordA | RecordB = Field(
27+
...,
28+
description='Minimal schema to reproduce reuse_model + collapse_root_models bugs in datamodel-code-generator 0.55.0',
29+
title='ReproBugs',
30+
)
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"title": "ReproBugs",
4+
"description": "Minimal schema to reproduce reuse_model + collapse_root_models bugs in datamodel-code-generator 0.55.0",
5+
"$defs": {
6+
"Shared": {
7+
"type": "object",
8+
"title": "Shared",
9+
"properties": {
10+
"shortCode": {
11+
"title": "ShortCode",
12+
"type": "string",
13+
"maxLength": 8,
14+
"minLength": 0
15+
}
16+
},
17+
"required": [
18+
"shortCode"
19+
]
20+
},
21+
"RecordA": {
22+
"type": "object",
23+
"title": "RecordA",
24+
"allOf": [
25+
{
26+
"$ref": "#/$defs/Shared"
27+
},
28+
{
29+
"type": "object",
30+
"properties": {
31+
"timestamp": {
32+
"title": "Timestamp",
33+
"type": "string"
34+
}
35+
},
36+
"required": [
37+
"timestamp"
38+
]
39+
}
40+
]
41+
},
42+
"RecordB": {
43+
"type": "object",
44+
"title": "RecordB",
45+
"allOf": [
46+
{
47+
"$ref": "#/$defs/Shared"
48+
},
49+
{
50+
"type": "object",
51+
"properties": {
52+
"created": {
53+
"title": "Timestamp",
54+
"type": "string"
55+
},
56+
"label": {
57+
"title": "LongText",
58+
"type": "string"
59+
}
60+
},
61+
"required": [
62+
"created",
63+
"label"
64+
]
65+
}
66+
]
67+
}
68+
},
69+
"oneOf": [
70+
{
71+
"$ref": "#/$defs/RecordA"
72+
},
73+
{
74+
"$ref": "#/$defs/RecordB"
75+
}
76+
]
77+
}

tests/main/jsonschema/test_main_jsonschema.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,37 @@ def test_main_reuse_model_collapse_nested(output_file: Path) -> None:
13381338
)
13391339

13401340

1341+
@pytest.mark.parametrize(
1342+
"collapse_reuse_model_args",
1343+
[[], ["--collapse-reuse-models"]],
1344+
)
1345+
def test_main_reuse_model_collapse_root_models_preserves_constraints(
1346+
output_file: Path, collapse_reuse_model_args: list[str]
1347+
) -> None:
1348+
"""Test root-model collapse keeps distinct field constraints when titles collide."""
1349+
extra_args = [
1350+
"--reuse-model",
1351+
"--collapse-root-models",
1352+
"--use-title-as-name",
1353+
"--field-constraints",
1354+
"--snake-case-field",
1355+
"--target-python-version",
1356+
"3.10",
1357+
"--output-model-type",
1358+
"pydantic_v2.BaseModel",
1359+
*collapse_reuse_model_args,
1360+
]
1361+
1362+
run_main_and_assert(
1363+
input_path=JSON_SCHEMA_DATA_PATH / "reuse_model_collapse_root_models_constraints.json",
1364+
output_path=output_file,
1365+
input_file_type="jsonschema",
1366+
assert_func=assert_file_content,
1367+
expected_file="reuse_model_collapse_root_models_constraints.py",
1368+
extra_args=extra_args,
1369+
)
1370+
1371+
13411372
@pytest.mark.cli_doc(
13421373
options=["--capitalize-enum-members"],
13431374
option_description="""Capitalize enum member names to UPPER_CASE format.

0 commit comments

Comments
 (0)