Skip to content

Commit 4745621

Browse files
committed
Fix reuse discriminator literals
1 parent 160e507 commit 4745621

4 files changed

Lines changed: 165 additions & 0 deletions

File tree

src/datamodel_code_generator/parser/base.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,6 +1618,14 @@ def get_discriminator_field_value(
16181618
if discriminator_value is not None:
16191619
discriminator_values = [discriminator_value]
16201620
break
1621+
if not discriminator_values and discriminator_model.path.endswith("/reuse"):
1622+
for discriminator_field in discriminator_model.iter_all_fields():
1623+
if field_name not in {discriminator_field.original_name, discriminator_field.name}:
1624+
continue
1625+
discriminator_value = get_discriminator_field_value(discriminator_field)
1626+
if discriminator_value is not None: # pragma: no branch
1627+
discriminator_values = [discriminator_value]
1628+
break
16211629

16221630
if not discriminator_values and mapping:
16231631
check_paths(discriminator_model, mapping) # ty: ignore
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# generated by datamodel-codegen:
2+
# filename: reuse_model_discriminator_literal.json
3+
# timestamp: 2019-07-26T00:00:00+00:00
4+
5+
from __future__ import annotations
6+
7+
from typing import Literal
8+
9+
from pydantic import BaseModel, Field
10+
11+
12+
class SignalId(BaseModel):
13+
domain: str
14+
source: Literal['catalog']
15+
16+
17+
class SignalId1(BaseModel):
18+
url: str
19+
source: Literal['agent']
20+
21+
22+
class FirstThing(BaseModel):
23+
signal_id: SignalId | SignalId1 | None = Field(
24+
None, discriminator='source', title='Signal ID'
25+
)
26+
27+
28+
class SignalId2(SignalId):
29+
source: Literal['catalog']
30+
31+
32+
class SignalId3(SignalId1):
33+
source: Literal['agent']
34+
35+
36+
class SecondThing(BaseModel):
37+
signal_id: SignalId2 | SignalId3 | None = Field(
38+
None, discriminator='source', title='Signal ID'
39+
)
40+
41+
42+
class Root(BaseModel):
43+
first_thing: FirstThing | None = None
44+
second_thing: SecondThing | None = None
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"title": "Root",
4+
"type": "object",
5+
"properties": {
6+
"first_thing": {
7+
"type": "object",
8+
"properties": {
9+
"signal_id": {
10+
"title": "Signal ID",
11+
"discriminator": {
12+
"propertyName": "source"
13+
},
14+
"oneOf": [
15+
{
16+
"type": "object",
17+
"properties": {
18+
"domain": {
19+
"type": "string"
20+
},
21+
"source": {
22+
"const": "catalog"
23+
}
24+
},
25+
"required": [
26+
"source",
27+
"domain"
28+
]
29+
},
30+
{
31+
"type": "object",
32+
"properties": {
33+
"url": {
34+
"type": "string"
35+
},
36+
"source": {
37+
"const": "agent"
38+
}
39+
},
40+
"required": [
41+
"source",
42+
"url"
43+
]
44+
}
45+
]
46+
}
47+
}
48+
},
49+
"second_thing": {
50+
"type": "object",
51+
"properties": {
52+
"signal_id": {
53+
"title": "Signal ID",
54+
"discriminator": {
55+
"propertyName": "source"
56+
},
57+
"oneOf": [
58+
{
59+
"type": "object",
60+
"properties": {
61+
"domain": {
62+
"type": "string"
63+
},
64+
"source": {
65+
"const": "catalog"
66+
}
67+
},
68+
"required": [
69+
"source",
70+
"domain"
71+
]
72+
},
73+
{
74+
"type": "object",
75+
"properties": {
76+
"url": {
77+
"type": "string"
78+
},
79+
"source": {
80+
"const": "agent"
81+
}
82+
},
83+
"required": [
84+
"source",
85+
"url"
86+
]
87+
}
88+
]
89+
}
90+
}
91+
}
92+
}
93+
}

tests/main/jsonschema/test_main_jsonschema.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,26 @@ def test_main_reuse_model_collapse_inline_definitions(output_file: Path) -> None
12141214
)
12151215

12161216

1217+
def test_main_reuse_model_discriminator_literal(output_file: Path) -> None:
1218+
"""Reuse inherited discriminator literals instead of injecting the reuse path segment."""
1219+
run_main_and_assert(
1220+
input_path=JSON_SCHEMA_DATA_PATH / "reuse_model_discriminator_literal.json",
1221+
output_path=output_file,
1222+
input_file_type="jsonschema",
1223+
assert_func=assert_file_content,
1224+
extra_args=[
1225+
"--reuse-model",
1226+
"--use-union-operator",
1227+
"--use-standard-collections",
1228+
"--output-model-type",
1229+
"pydantic_v2.BaseModel",
1230+
"--target-python-version",
1231+
"3.10",
1232+
],
1233+
force_exec_validation=True,
1234+
)
1235+
1236+
12171237
def test_main_reuse_model_collapse_with_root(output_file: Path) -> None:
12181238
"""Test --reuse-model --collapse-reuse-models skips RootModel deduplication."""
12191239
run_main_and_assert(

0 commit comments

Comments
 (0)