Skip to content

Commit 2b04a28

Browse files
authored
fix: missing default value in Pydantic generated code when using polymorphism (#2271)
1 parent 0474e30 commit 2b04a28

File tree

3 files changed

+158
-0
lines changed

3 files changed

+158
-0
lines changed

src/generators/python/presets/Pydantic.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ const PYTHON_PYDANTIC_CLASS_PRESET: ClassPresetType<PythonOptions> = {
5353
if (isOptional) {
5454
decoratorArgs.push('default=None');
5555
}
56+
if (property.property.options.const) {
57+
decoratorArgs.push(`default=${property.property.options.const.value}`);
58+
decoratorArgs.push('frozen=True');
59+
}
5660
if (
5761
property.property instanceof ConstrainedDictionaryModel &&
5862
property.property.serializationType === 'unwrap'

test/generators/python/presets/Pydantic.spec.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,73 @@ describe('PYTHON_PYDANTIC_PRESET', () => {
9494
const models = await generator.generate(doc);
9595
expect(models.map((model) => model.result)).toMatchSnapshot();
9696
});
97+
98+
test('should render default value for discriminator when using polymorphism', async () => {
99+
const doc = {
100+
asyncapi: '3.0.0',
101+
info: {
102+
title: 'Vehicle Models',
103+
version: '1.0.0'
104+
},
105+
components: {
106+
messages: {
107+
Vehicle: {
108+
payload: {
109+
oneOf: [
110+
{ $ref: '#/components/schemas/Car' },
111+
{ $ref: '#/components/schemas/Truck' }
112+
]
113+
}
114+
}
115+
},
116+
schemas: {
117+
Vehicle: {
118+
title: 'Vehicle',
119+
type: 'object',
120+
discriminator: 'vehicleType',
121+
properties: {
122+
vehicleType: {
123+
title: 'VehicleType',
124+
type: 'string'
125+
},
126+
length: {
127+
type: 'number',
128+
format: 'float'
129+
}
130+
},
131+
required: ['vehicleType']
132+
},
133+
Car: {
134+
allOf: [
135+
{ $ref: '#/components/schemas/Vehicle' },
136+
{
137+
type: 'object',
138+
properties: {
139+
vehicleType: {
140+
const: 'Car'
141+
}
142+
}
143+
}
144+
]
145+
},
146+
Truck: {
147+
allOf: [
148+
{ $ref: '#/components/schemas/Vehicle' },
149+
{
150+
type: 'object',
151+
properties: {
152+
vehicleType: {
153+
const: 'Truck'
154+
}
155+
}
156+
}
157+
]
158+
}
159+
}
160+
}
161+
};
162+
163+
const models = await generator.generate(doc);
164+
expect(models.map((model) => model.result)).toMatchSnapshot();
165+
});
97166
});

test/generators/python/presets/__snapshots__/Pydantic.spec.ts.snap

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,90 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`PYTHON_PYDANTIC_PRESET should render default value for discriminator when using polymorphism 1`] = `
4+
Array [
5+
"",
6+
"class Car(BaseModel):
7+
vehicle_type: VehicleType.VehicleType = Field(default=VehicleType.VehicleType.CAR, frozen=True)
8+
length: Optional[float] = Field(default=None)
9+
additional_properties: Optional[dict[str, Any]] = Field(default=None, exclude=True)
10+
11+
@model_serializer(mode='wrap')
12+
def custom_serializer(self, handler):
13+
serialized_self = handler(self)
14+
additional_properties = getattr(self, \\"additional_properties\\")
15+
if additional_properties is not None:
16+
for key, value in additional_properties.items():
17+
# Never overwrite existing values, to avoid clashes
18+
if not hasattr(serialized_self, key):
19+
serialized_self[key] = value
20+
21+
return serialized_self
22+
23+
@model_validator(mode='before')
24+
@classmethod
25+
def unwrap_additional_properties(cls, data):
26+
if not isinstance(data, dict):
27+
data = data.model_dump()
28+
json_properties = list(data.keys())
29+
known_object_properties = ['vehicle_type', 'length', 'additional_properties']
30+
unknown_object_properties = [element for element in json_properties if element not in known_object_properties]
31+
# Ignore attempts that validate regular models, only when unknown input is used we add unwrap extensions
32+
if len(unknown_object_properties) == 0:
33+
return data
34+
35+
known_json_properties = ['vehicleType', 'length', 'additionalProperties']
36+
additional_properties = data.get('additional_properties', {})
37+
for obj_key in list(data.keys()):
38+
if not known_json_properties.__contains__(obj_key):
39+
additional_properties[obj_key] = data.pop(obj_key, None)
40+
data['additional_properties'] = additional_properties
41+
return data
42+
43+
",
44+
"class VehicleType(Enum):
45+
CAR = \\"Car\\"
46+
TRUCK = \\"Truck\\"",
47+
"class Truck(BaseModel):
48+
vehicle_type: VehicleType.VehicleType = Field(default=VehicleType.VehicleType.TRUCK, frozen=True)
49+
length: Optional[float] = Field(default=None)
50+
additional_properties: Optional[dict[str, Any]] = Field(default=None, exclude=True)
51+
52+
@model_serializer(mode='wrap')
53+
def custom_serializer(self, handler):
54+
serialized_self = handler(self)
55+
additional_properties = getattr(self, \\"additional_properties\\")
56+
if additional_properties is not None:
57+
for key, value in additional_properties.items():
58+
# Never overwrite existing values, to avoid clashes
59+
if not hasattr(serialized_self, key):
60+
serialized_self[key] = value
61+
62+
return serialized_self
63+
64+
@model_validator(mode='before')
65+
@classmethod
66+
def unwrap_additional_properties(cls, data):
67+
if not isinstance(data, dict):
68+
data = data.model_dump()
69+
json_properties = list(data.keys())
70+
known_object_properties = ['vehicle_type', 'length', 'additional_properties']
71+
unknown_object_properties = [element for element in json_properties if element not in known_object_properties]
72+
# Ignore attempts that validate regular models, only when unknown input is used we add unwrap extensions
73+
if len(unknown_object_properties) == 0:
74+
return data
75+
76+
known_json_properties = ['vehicleType', 'length', 'additionalProperties']
77+
additional_properties = data.get('additional_properties', {})
78+
for obj_key in list(data.keys()):
79+
if not known_json_properties.__contains__(obj_key):
80+
additional_properties[obj_key] = data.pop(obj_key, None)
81+
data['additional_properties'] = additional_properties
82+
return data
83+
84+
",
85+
]
86+
`;
87+
388
exports[`PYTHON_PYDANTIC_PRESET should render nullable union 1`] = `
489
Array [
590
"class NullableUnionTest(BaseModel):

0 commit comments

Comments
 (0)