Skip to content

Commit b13e908

Browse files
[Python] Some fixes for pydantic v1 templates for nested dicts (#23162)
* Fix to_dict method * Fix union type hint * Use more concise code for dict generation * Add from dict method for array of dicts * Fix after reviewer comments * add check for none * Update samples
1 parent 5c6ab3e commit b13e908

27 files changed

Lines changed: 172 additions & 163 deletions

modules/openapi-generator/src/main/resources/python-pydantic-v1/model_generic.mustache

Lines changed: 62 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
126126
return json.dumps(self.to_dict())
127127

128128
@classmethod
129-
def from_json(cls, json_str: str) -> {{^hasChildren}}{{{classname}}}{{/hasChildren}}{{#hasChildren}}{{#discriminator}}Union({{#children}}{{{classname}}}{{^-last}}, {{/-last}}{{/children}}){{/discriminator}}{{^discriminator}}{{{classname}}}{{/discriminator}}{{/hasChildren}}:
129+
def from_json(cls, json_str: str) -> {{^hasChildren}}{{{classname}}}{{/hasChildren}}{{#hasChildren}}{{#discriminator}}Union[{{#children}}{{{classname}}}{{^-last}}, {{/-last}}{{/children}}]{{/discriminator}}{{^discriminator}}{{{classname}}}{{/discriminator}}{{/hasChildren}}:
130130
"""Create an instance of {{{classname}}} from a JSON string"""
131131
return cls.from_dict(json.loads(json_str))
132132

@@ -145,8 +145,9 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
145145
{{#allVars}}
146146
{{#isContainer}}
147147
{{#isArray}}
148-
{{#items.isArray}}
148+
{{#items.isContainer}}
149149
{{^items.items.isPrimitiveType}}
150+
{{#items.isArray}}
150151
# override the default output from pydantic by calling `to_dict()` of each item in {{{name}}} (list of list)
151152
_items = []
152153
if self.{{{name}}}:
@@ -156,9 +157,21 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
156157
[_inner_item.to_dict() for _inner_item in _item if _inner_item is not None]
157158
)
158159
_dict['{{{baseName}}}'] = _items
159-
{{/items.items.isPrimitiveType}}
160160
{{/items.isArray}}
161-
{{^items.isArray}}
161+
{{#items.isMap}}
162+
# override the default output from pydantic by calling `to_dict()` of each item in {{{name}}} (list of map)
163+
_items = []
164+
if self.{{{name}}}:
165+
for _item in self.{{{name}}}:
166+
if _item:
167+
_items.append(
168+
{_inner_key: _inner_value.to_dict() for _inner_key, _inner_value in _item.items() if _inner_value is not None}
169+
)
170+
_dict['{{{baseName}}}'] = _items
171+
{{/items.isMap}}
172+
{{/items.items.isPrimitiveType}}
173+
{{/items.isContainer}}
174+
{{^items.isContainer}}
162175
{{^items.isPrimitiveType}}
163176
{{^items.isEnumOrRef}}
164177
# override the default output from pydantic by calling `to_dict()` of each item in {{{name}}} (list)
@@ -170,11 +183,12 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
170183
_dict['{{{baseName}}}'] = _items
171184
{{/items.isEnumOrRef}}
172185
{{/items.isPrimitiveType}}
173-
{{/items.isArray}}
186+
{{/items.isContainer}}
174187
{{/isArray}}
175188
{{#isMap}}
176-
{{#items.isArray}}
189+
{{#items.isContainer}}
177190
{{^items.items.isPrimitiveType}}
191+
{{#items.isArray}}
178192
# override the default output from pydantic by calling `to_dict()` of each value in {{{name}}} (dict of array)
179193
_field_dict_of_array = {}
180194
if self.{{{name}}}:
@@ -184,9 +198,21 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
184198
_item.to_dict() for _item in self.{{{name}}}[_key]
185199
]
186200
_dict['{{{baseName}}}'] = _field_dict_of_array
187-
{{/items.items.isPrimitiveType}}
188201
{{/items.isArray}}
189-
{{^items.isArray}}
202+
{{#items.isMap}}
203+
# override the default output from pydantic by calling `to_dict()` of each value in {{{name}}} (dict of dict)
204+
_field_dict_of_dict = {}
205+
if self.{{{name}}}:
206+
for _key, _value in self.{{{name}}}.items():
207+
if _value is not None:
208+
_field_dict_of_dict[_key] = {
209+
_inner_key: _inner_value.to_dict() for _inner_key, _inner_value in _value.items() if _inner_value is not None
210+
}
211+
_dict['{{{baseName}}}'] = _field_dict_of_dict
212+
{{/items.isMap}}
213+
{{/items.items.isPrimitiveType}}
214+
{{/items.isContainer}}
215+
{{^items.isContainer}}
190216
{{^items.isPrimitiveType}}
191217
{{^items.isEnumOrRef}}
192218
# override the default output from pydantic by calling `to_dict()` of each value in {{{name}}} (dict)
@@ -198,7 +224,7 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
198224
_dict['{{{baseName}}}'] = _field_dict
199225
{{/items.isEnumOrRef}}
200226
{{/items.isPrimitiveType}}
201-
{{/items.isArray}}
227+
{{/items.isContainer}}
202228
{{/isMap}}
203229
{{/isContainer}}
204230
{{^isContainer}}
@@ -230,7 +256,7 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
230256
return _dict
231257

232258
@classmethod
233-
def from_dict(cls, obj: dict) -> {{^hasChildren}}{{{classname}}}{{/hasChildren}}{{#hasChildren}}{{#discriminator}}Union({{#children}}{{{classname}}}{{^-last}}, {{/-last}}{{/children}}){{/discriminator}}{{^discriminator}}{{{classname}}}{{/discriminator}}{{/hasChildren}}:
259+
def from_dict(cls, obj: dict) -> {{^hasChildren}}{{{classname}}}{{/hasChildren}}{{#hasChildren}}{{#discriminator}}Union[{{#children}}{{{classname}}}{{^-last}}, {{/-last}}{{/children}}]{{/discriminator}}{{^discriminator}}{{{classname}}}{{/discriminator}}{{/hasChildren}}:
234260
"""Create an instance of {{{classname}}} from a dict"""
235261
{{#hasChildren}}
236262
{{#discriminator}}
@@ -265,18 +291,26 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
265291
{{#allVars}}
266292
{{#isContainer}}
267293
{{#isArray}}
268-
{{#items.isArray}}
294+
{{#items.isContainer}}
269295
{{#items.items.isPrimitiveType}}
270296
"{{{name}}}": obj.get("{{{baseName}}}"){{^-last}},{{/-last}}
271297
{{/items.items.isPrimitiveType}}
272298
{{^items.items.isPrimitiveType}}
299+
{{#items.isArray}}
273300
"{{{name}}}": [
274301
[{{{items.items.dataType}}}.from_dict(_inner_item) for _inner_item in _item]
275302
for _item in obj.get("{{{baseName}}}")
276303
] if obj.get("{{{baseName}}}") is not None else None{{^-last}},{{/-last}}
277-
{{/items.items.isPrimitiveType}}
278304
{{/items.isArray}}
279-
{{^items.isArray}}
305+
{{#items.isMap}}
306+
"{{{name}}}": [
307+
{_inner_key: {{{items.items.dataType}}}.from_dict(_inner_value) for _inner_key, _inner_value in _item.items()}
308+
for _item in obj.get("{{{baseName}}}")
309+
] if obj.get("{{{baseName}}}") is not None else None{{^-last}},{{/-last}}
310+
{{/items.isMap}}
311+
{{/items.items.isPrimitiveType}}
312+
{{/items.isContainer}}
313+
{{^items.isContainer}}
280314
{{^items.isPrimitiveType}}
281315
{{#items.isEnumOrRef}}
282316
"{{{name}}}": obj.get("{{{baseName}}}"){{^-last}},{{/-last}}
@@ -288,48 +322,39 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
288322
{{#items.isPrimitiveType}}
289323
"{{{name}}}": obj.get("{{{baseName}}}"){{^-last}},{{/-last}}
290324
{{/items.isPrimitiveType}}
291-
{{/items.isArray}}
325+
{{/items.isContainer}}
292326
{{/isArray}}
293327
{{#isMap}}
294328
{{^items.isPrimitiveType}}
295329
{{^items.isEnumOrRef}}
296330
{{#items.isContainer}}
297331
{{#items.isMap}}
298-
"{{{name}}}": dict(
299-
(_k, dict(
300-
(_ik, {{{items.items.dataType}}}.from_dict(_iv))
301-
for _ik, _iv in _v.items()
302-
)
303-
if _v is not None
304-
else None
305-
)
306-
for _k, _v in obj.get("{{{baseName}}}").items()
307-
)
332+
"{{{name}}}": {
333+
_k: {_ik: {{{items.items.dataType}}}.from_dict(_iv) for _ik, _iv in _v.items()} if _v is not None else None
334+
for _k, _v in obj["{{{baseName}}}"].items()
335+
}
308336
if obj.get("{{{baseName}}}") is not None
309337
else None{{^-last}},{{/-last}}
310338
{{/items.isMap}}
311339
{{#items.isArray}}
312-
"{{{name}}}": dict(
313-
(_k,
314-
[{{{items.items.dataType}}}.from_dict(_item) for _item in _v]
315-
if _v is not None
316-
else None
317-
)
318-
for _k, _v in obj.get("{{{baseName}}}").items()
319-
){{^-last}},{{/-last}}
340+
"{{{name}}}": {
341+
_k: [{{{items.items.dataType}}}.from_dict(_item) for _item in _v] if _v is not None else None
342+
for _k, _v in obj["{{{baseName}}}"].items()
343+
}
344+
if obj.get("{{{baseName}}}") is not None
345+
else None{{^-last}},{{/-last}}
320346
{{/items.isArray}}
321347
{{/items.isContainer}}
322348
{{^items.isContainer}}
323-
"{{{name}}}": dict(
324-
(_k, {{{items.dataType}}}.from_dict(_v))
325-
for _k, _v in obj.get("{{{baseName}}}").items()
326-
)
349+
"{{{name}}}": {
350+
_k: {{{items.dataType}}}.from_dict(_v) for _k, _v in obj["{{{baseName}}}"].items()
351+
}
327352
if obj.get("{{{baseName}}}") is not None
328353
else None{{^-last}},{{/-last}}
329354
{{/items.isContainer}}
330355
{{/items.isEnumOrRef}}
331356
{{#items.isEnumOrRef}}
332-
"{{{name}}}": dict((_k, _v) for _k, _v in obj.get("{{{baseName}}}").items()){{^-last}},{{/-last}}
357+
"{{{name}}}": obj.get("{{{baseName}}}"){{^-last}},{{/-last}}
333358
{{/items.isEnumOrRef}}
334359
{{/items.isPrimitiveType}}
335360
{{#items.isPrimitiveType}}

samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/petstore_api/models/additional_properties_class.py

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,15 @@ def to_dict(self):
5555
exclude={
5656
},
5757
exclude_none=True)
58-
# override the default output from pydantic by calling `to_dict()` of each value in map_of_map_non_primitive_property (dict)
59-
_field_dict = {}
58+
# override the default output from pydantic by calling `to_dict()` of each value in map_of_map_non_primitive_property (dict of dict)
59+
_field_dict_of_dict = {}
6060
if self.map_of_map_non_primitive_property:
61-
for _key in self.map_of_map_non_primitive_property:
62-
if self.map_of_map_non_primitive_property[_key]:
63-
_field_dict[_key] = self.map_of_map_non_primitive_property[_key].to_dict()
64-
_dict['map_of_map_non_primitive_property'] = _field_dict
61+
for _key, _value in self.map_of_map_non_primitive_property.items():
62+
if _value is not None:
63+
_field_dict_of_dict[_key] = {
64+
_inner_key: _inner_value.to_dict() for _inner_key, _inner_value in _value.items() if _inner_value is not None
65+
}
66+
_dict['map_of_map_non_primitive_property'] = _field_dict_of_dict
6567
return _dict
6668

6769
@classmethod
@@ -76,16 +78,10 @@ def from_dict(cls, obj: dict) -> AdditionalPropertiesClass:
7678
_obj = AdditionalPropertiesClass.parse_obj({
7779
"map_property": obj.get("map_property"),
7880
"map_of_map_property": obj.get("map_of_map_property"),
79-
"map_of_map_non_primitive_property": dict(
80-
(_k, dict(
81-
(_ik, Pet.from_dict(_iv))
82-
for _ik, _iv in _v.items()
83-
)
84-
if _v is not None
85-
else None
86-
)
87-
for _k, _v in obj.get("map_of_map_non_primitive_property").items()
88-
)
81+
"map_of_map_non_primitive_property": {
82+
_k: {_ik: Pet.from_dict(_iv) for _ik, _iv in _v.items()} if _v is not None else None
83+
for _k, _v in obj["map_of_map_non_primitive_property"].items()
84+
}
8985
if obj.get("map_of_map_non_primitive_property") is not None
9086
else None
9187
})

samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/petstore_api/models/animal.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def to_json(self) -> str:
6767
return json.dumps(self.to_dict())
6868

6969
@classmethod
70-
def from_json(cls, json_str: str) -> Union(Cat, Dog):
70+
def from_json(cls, json_str: str) -> Union[Cat, Dog]:
7171
"""Create an instance of Animal from a JSON string"""
7272
return cls.from_dict(json.loads(json_str))
7373

@@ -80,7 +80,7 @@ def to_dict(self):
8080
return _dict
8181

8282
@classmethod
83-
def from_dict(cls, obj: dict) -> Union(Cat, Dog):
83+
def from_dict(cls, obj: dict) -> Union[Cat, Dog]:
8484
"""Create an instance of Animal from a dict"""
8585
# look up the object type based on discriminator mapping
8686
object_type = cls.get_discriminator_value(obj)

samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/petstore_api/models/array_of_map_model.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,14 @@ def to_dict(self):
5353
exclude={
5454
},
5555
exclude_none=True)
56-
# override the default output from pydantic by calling `to_dict()` of each item in array_of_map_property (list)
56+
# override the default output from pydantic by calling `to_dict()` of each item in array_of_map_property (list of map)
5757
_items = []
5858
if self.array_of_map_property:
5959
for _item in self.array_of_map_property:
6060
if _item:
61-
_items.append(_item.to_dict())
61+
_items.append(
62+
{_inner_key: _inner_value.to_dict() for _inner_key, _inner_value in _item.items() if _inner_value is not None}
63+
)
6264
_dict['array_of_map_property'] = _items
6365
return _dict
6466

@@ -72,7 +74,10 @@ def from_dict(cls, obj: dict) -> ArrayOfMapModel:
7274
return ArrayOfMapModel.parse_obj(obj)
7375

7476
_obj = ArrayOfMapModel.parse_obj({
75-
"array_of_map_property": [Dict[str, Tag].from_dict(_item) for _item in obj.get("array_of_map_property")] if obj.get("array_of_map_property") is not None else None
77+
"array_of_map_property": [
78+
{_inner_key: Tag.from_dict(_inner_value) for _inner_key, _inner_value in _item.items()}
79+
for _item in obj.get("array_of_map_property")
80+
] if obj.get("array_of_map_property") is not None else None
7681
})
7782
return _obj
7883

samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/petstore_api/models/base_discriminator.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def to_json(self) -> str:
6666
return json.dumps(self.to_dict())
6767

6868
@classmethod
69-
def from_json(cls, json_str: str) -> Union(Info, PrimitiveString):
69+
def from_json(cls, json_str: str) -> Union[Info, PrimitiveString]:
7070
"""Create an instance of BaseDiscriminator from a JSON string"""
7171
return cls.from_dict(json.loads(json_str))
7272

@@ -79,7 +79,7 @@ def to_dict(self):
7979
return _dict
8080

8181
@classmethod
82-
def from_dict(cls, obj: dict) -> Union(Info, PrimitiveString):
82+
def from_dict(cls, obj: dict) -> Union[Info, PrimitiveString]:
8383
"""Create an instance of BaseDiscriminator from a dict"""
8484
# look up the object type based on discriminator mapping
8585
object_type = cls.get_discriminator_value(obj)

samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/petstore_api/models/creature.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def to_json(self) -> str:
6666
return json.dumps(self.to_dict())
6767

6868
@classmethod
69-
def from_json(cls, json_str: str) -> Union(HuntingDog):
69+
def from_json(cls, json_str: str) -> Union[HuntingDog]:
7070
"""Create an instance of Creature from a JSON string"""
7171
return cls.from_dict(json.loads(json_str))
7272

@@ -82,7 +82,7 @@ def to_dict(self):
8282
return _dict
8383

8484
@classmethod
85-
def from_dict(cls, obj: dict) -> Union(HuntingDog):
85+
def from_dict(cls, obj: dict) -> Union[HuntingDog]:
8686
"""Create an instance of Creature from a dict"""
8787
# look up the object type based on discriminator mapping
8888
object_type = cls.get_discriminator_value(obj)

samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/petstore_api/models/discriminator_all_of_super.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def to_json(self) -> str:
6464
return json.dumps(self.to_dict())
6565

6666
@classmethod
67-
def from_json(cls, json_str: str) -> Union(DiscriminatorAllOfSub):
67+
def from_json(cls, json_str: str) -> Union[DiscriminatorAllOfSub]:
6868
"""Create an instance of DiscriminatorAllOfSuper from a JSON string"""
6969
return cls.from_dict(json.loads(json_str))
7070

@@ -77,7 +77,7 @@ def to_dict(self):
7777
return _dict
7878

7979
@classmethod
80-
def from_dict(cls, obj: dict) -> Union(DiscriminatorAllOfSub):
80+
def from_dict(cls, obj: dict) -> Union[DiscriminatorAllOfSub]:
8181
"""Create an instance of DiscriminatorAllOfSuper from a dict"""
8282
# look up the object type based on discriminator mapping
8383
object_type = cls.get_discriminator_value(obj)

samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/petstore_api/models/input_all_of.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,9 @@ def from_dict(cls, obj: dict) -> InputAllOf:
7272
return InputAllOf.parse_obj(obj)
7373

7474
_obj = InputAllOf.parse_obj({
75-
"some_data": dict(
76-
(_k, Tag.from_dict(_v))
77-
for _k, _v in obj.get("some_data").items()
78-
)
75+
"some_data": {
76+
_k: Tag.from_dict(_v) for _k, _v in obj["some_data"].items()
77+
}
7978
if obj.get("some_data") is not None
8079
else None
8180
})

samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/petstore_api/models/map_of_array_of_model.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,12 @@ def from_dict(cls, obj: dict) -> MapOfArrayOfModel:
7474
return MapOfArrayOfModel.parse_obj(obj)
7575

7676
_obj = MapOfArrayOfModel.parse_obj({
77-
"shop_id_to_org_online_lip_map": dict(
78-
(_k,
79-
[Tag.from_dict(_item) for _item in _v]
80-
if _v is not None
81-
else None
82-
)
83-
for _k, _v in obj.get("shopIdToOrgOnlineLipMap").items()
84-
)
77+
"shop_id_to_org_online_lip_map": {
78+
_k: [Tag.from_dict(_item) for _item in _v] if _v is not None else None
79+
for _k, _v in obj["shopIdToOrgOnlineLipMap"].items()
80+
}
81+
if obj.get("shopIdToOrgOnlineLipMap") is not None
82+
else None
8583
})
8684
return _obj
8785

samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/petstore_api/models/mixed_properties_and_additional_properties_class.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,9 @@ def from_dict(cls, obj: dict) -> MixedPropertiesAndAdditionalPropertiesClass:
7676
_obj = MixedPropertiesAndAdditionalPropertiesClass.parse_obj({
7777
"uuid": obj.get("uuid"),
7878
"date_time": obj.get("dateTime"),
79-
"map": dict(
80-
(_k, Animal.from_dict(_v))
81-
for _k, _v in obj.get("map").items()
82-
)
79+
"map": {
80+
_k: Animal.from_dict(_v) for _k, _v in obj["map"].items()
81+
}
8382
if obj.get("map") is not None
8483
else None
8584
})

0 commit comments

Comments
 (0)