Skip to content

Commit

Permalink
[Python] Bug Fix - model_generic templates to have valid imports for …
Browse files Browse the repository at this point in the history
…polymorphism (#20273)

* fix model_generic python templates to have valid imports for polymorphism

* update samples

* update samples

---------

Co-authored-by: yugi <[email protected]>
  • Loading branch information
YousefHaggy and yugi authored Jan 9, 2025
1 parent 3d65786 commit 7a23ac7
Show file tree
Hide file tree
Showing 35 changed files with 850 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ from typing import TYPE_CHECKING
from importlib import import_module
if TYPE_CHECKING:
{{#mappedModels}}
from {{packageName}}.models.{{model.classVarName}} import {{modelName}}
from {{packageName}}.models.{{model.classFilename}} import {{modelName}}
{{/mappedModels}}

{{/discriminator}}
Expand Down Expand Up @@ -238,7 +238,7 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
object_type = cls.get_discriminator_value(obj)
{{#mappedModels}}
if object_type == '{{{modelName}}}':
return import_module("{{packageName}}.models.{{model.classVarName}}").{{modelName}}.from_dict(obj)
return import_module("{{packageName}}.models.{{model.classFilename}}").{{modelName}}.from_dict(obj)
{{/mappedModels}}
raise ValueError("{{{classname}}} failed to lookup discriminator value from " +
json.dumps(obj) + ". Discriminator property name: " + cls.__discriminator_property_name +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ from typing_extensions import Self
from typing import TYPE_CHECKING
if TYPE_CHECKING:
{{#mappedModels}}
from {{packageName}}.models.{{model.classVarName}} import {{modelName}}
from {{packageName}}.models.{{model.classFilename}} import {{modelName}}
{{/mappedModels}}

{{/discriminator}}
Expand Down Expand Up @@ -261,7 +261,7 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
object_type = cls.get_discriminator_value(obj)
{{#mappedModels}}
if object_type == '{{{modelName}}}':
return import_module("{{packageName}}.models.{{model.classVarName}}").{{modelName}}.from_dict(obj)
return import_module("{{packageName}}.models.{{model.classFilename}}").{{modelName}}.from_dict(obj)
{{/mappedModels}}

raise ValueError("{{{classname}}} failed to lookup discriminator value from " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1950,6 +1950,13 @@ components:
properties:
_class:
type: string
Hunting__Dog:
allOf:
- $ref: '#/components/schemas/Creature'
- type: object
properties:
isTrained:
type: boolean
Dog:
allOf:
- $ref: '#/components/schemas/Animal'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ docs/FooGetDefaultResponse.md
docs/FormatTest.md
docs/HasOnlyReadOnly.md
docs/HealthCheckResult.md
docs/HuntingDog.md
docs/ImportTestDatetimeApi.md
docs/Info.md
docs/InnerDictWithProperty.md
Expand Down Expand Up @@ -178,6 +179,7 @@ petstore_api/models/foo_get_default_response.py
petstore_api/models/format_test.py
petstore_api/models/has_only_read_only.py
petstore_api/models/health_check_result.py
petstore_api/models/hunting_dog.py
petstore_api/models/info.py
petstore_api/models/inner_dict_with_property.py
petstore_api/models/input_all_of.py
Expand Down
1 change: 1 addition & 0 deletions samples/openapi3/client/petstore/python-aiohttp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ Class | Method | HTTP request | Description
- [FormatTest](docs/FormatTest.md)
- [HasOnlyReadOnly](docs/HasOnlyReadOnly.md)
- [HealthCheckResult](docs/HealthCheckResult.md)
- [HuntingDog](docs/HuntingDog.md)
- [Info](docs/Info.md)
- [InnerDictWithProperty](docs/InnerDictWithProperty.md)
- [InputAllOf](docs/InputAllOf.md)
Expand Down
29 changes: 29 additions & 0 deletions samples/openapi3/client/petstore/python-aiohttp/docs/HuntingDog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# HuntingDog


## Properties

Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**is_trained** | **bool** | | [optional]

## Example

```python
from petstore_api.models.hunting_dog import HuntingDog

# TODO update the JSON string below
json = "{}"
# create an instance of HuntingDog from a JSON string
hunting_dog_instance = HuntingDog.from_json(json)
# print the JSON string representation of the object
print(HuntingDog.to_json())

# convert the object into a dict
hunting_dog_dict = hunting_dog_instance.to_dict()
# create an instance of HuntingDog from a dict
hunting_dog_from_dict = HuntingDog.from_dict(hunting_dog_dict)
```
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)


Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
from petstore_api.models.format_test import FormatTest
from petstore_api.models.has_only_read_only import HasOnlyReadOnly
from petstore_api.models.health_check_result import HealthCheckResult
from petstore_api.models.hunting_dog import HuntingDog
from petstore_api.models.info import Info
from petstore_api.models.inner_dict_with_property import InnerDictWithProperty
from petstore_api.models.input_all_of import InputAllOf
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
from petstore_api.models.format_test import FormatTest
from petstore_api.models.has_only_read_only import HasOnlyReadOnly
from petstore_api.models.health_check_result import HealthCheckResult
from petstore_api.models.hunting_dog import HuntingDog
from petstore_api.models.info import Info
from petstore_api.models.inner_dict_with_property import InnerDictWithProperty
from petstore_api.models.input_all_of import InputAllOf
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,17 @@
import re # noqa: F401
import json

from importlib import import_module
from pydantic import BaseModel, ConfigDict, StrictStr
from typing import Any, ClassVar, Dict, List
from typing import Any, ClassVar, Dict, List, Union
from petstore_api.models.creature_info import CreatureInfo
from typing import Optional, Set
from typing_extensions import Self

from typing import TYPE_CHECKING
if TYPE_CHECKING:
from petstore_api.models.hunting_dog import HuntingDog

class Creature(BaseModel):
"""
Creature
Expand All @@ -38,6 +43,23 @@ class Creature(BaseModel):
)


# JSON field name that stores the object type
__discriminator_property_name: ClassVar[str] = 'type'

# discriminator mappings
__discriminator_value_class_map: ClassVar[Dict[str, str]] = {
'Hunting__Dog': 'HuntingDog'
}

@classmethod
def get_discriminator_value(cls, obj: Dict[str, Any]) -> Optional[str]:
"""Returns the discriminator value (object type) of the data"""
discriminator_value = obj[cls.__discriminator_property_name]
if discriminator_value:
return cls.__discriminator_value_class_map.get(discriminator_value)
else:
return None

def to_str(self) -> str:
"""Returns the string representation of the model using alias"""
return pprint.pformat(self.model_dump(by_alias=True))
Expand All @@ -48,7 +70,7 @@ def to_json(self) -> str:
return json.dumps(self.to_dict())

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

Expand Down Expand Up @@ -76,18 +98,15 @@ def to_dict(self) -> Dict[str, Any]:
return _dict

@classmethod
def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
def from_dict(cls, obj: Dict[str, Any]) -> Optional[Union[HuntingDog]]:
"""Create an instance of Creature from a dict"""
if obj is None:
return None

if not isinstance(obj, dict):
return cls.model_validate(obj)

_obj = cls.model_validate({
"info": CreatureInfo.from_dict(obj["info"]) if obj.get("info") is not None else None,
"type": obj.get("type")
})
return _obj
# look up the object type based on discriminator mapping
object_type = cls.get_discriminator_value(obj)
if object_type == 'HuntingDog':
return import_module("petstore_api.models.hunting_dog").HuntingDog.from_dict(obj)

raise ValueError("Creature failed to lookup discriminator value from " +
json.dumps(obj) + ". Discriminator property name: " + cls.__discriminator_property_name +
", mapping: " + json.dumps(cls.__discriminator_value_class_map))


Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# coding: utf-8

"""
OpenAPI Petstore
This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\
The version of the OpenAPI document: 1.0.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
""" # noqa: E501


from __future__ import annotations
import pprint
import re # noqa: F401
import json

from pydantic import ConfigDict, Field, StrictBool
from typing import Any, ClassVar, Dict, List, Optional
from petstore_api.models.creature import Creature
from petstore_api.models.creature_info import CreatureInfo
from typing import Optional, Set
from typing_extensions import Self

class HuntingDog(Creature):
"""
HuntingDog
""" # noqa: E501
is_trained: Optional[StrictBool] = Field(default=None, alias="isTrained")
__properties: ClassVar[List[str]] = ["info", "type", "isTrained"]

model_config = ConfigDict(
populate_by_name=True,
validate_assignment=True,
protected_namespaces=(),
)


def to_str(self) -> str:
"""Returns the string representation of the model using alias"""
return pprint.pformat(self.model_dump(by_alias=True))

def to_json(self) -> str:
"""Returns the JSON representation of the model using alias"""
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
return json.dumps(self.to_dict())

@classmethod
def from_json(cls, json_str: str) -> Optional[Self]:
"""Create an instance of HuntingDog from a JSON string"""
return cls.from_dict(json.loads(json_str))

def to_dict(self) -> Dict[str, Any]:
"""Return the dictionary representation of the model using alias.
This has the following differences from calling pydantic's
`self.model_dump(by_alias=True)`:
* `None` is only added to the output dict for nullable fields that
were set at model initialization. Other fields with value `None`
are ignored.
"""
excluded_fields: Set[str] = set([
])

_dict = self.model_dump(
by_alias=True,
exclude=excluded_fields,
exclude_none=True,
)
# override the default output from pydantic by calling `to_dict()` of info
if self.info:
_dict['info'] = self.info.to_dict()
return _dict

@classmethod
def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
"""Create an instance of HuntingDog from a dict"""
if obj is None:
return None

if not isinstance(obj, dict):
return cls.model_validate(obj)

_obj = cls.model_validate({
"info": CreatureInfo.from_dict(obj["info"]) if obj.get("info") is not None else None,
"type": obj.get("type"),
"isTrained": obj.get("isTrained")
})
return _obj


Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# coding: utf-8

"""
OpenAPI Petstore
This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\
The version of the OpenAPI document: 1.0.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
""" # noqa: E501


import unittest

from petstore_api.models.hunting_dog import HuntingDog

class TestHuntingDog(unittest.TestCase):
"""HuntingDog unit test stubs"""

def setUp(self):
pass

def tearDown(self):
pass

def make_instance(self, include_optional) -> HuntingDog:
"""Test HuntingDog
include_optional is a boolean, when False only required
params are included, when True both required and
optional params are included """
# uncomment below to create an instance of `HuntingDog`
"""
model = HuntingDog()
if include_optional:
return HuntingDog(
is_trained = True
)
else:
return HuntingDog(
)
"""

def testHuntingDog(self):
"""Test HuntingDog"""
# inst_req_only = self.make_instance(include_optional=False)
# inst_req_and_optional = self.make_instance(include_optional=True)

if __name__ == '__main__':
unittest.main()
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ docs/FooGetDefaultResponse.md
docs/FormatTest.md
docs/HasOnlyReadOnly.md
docs/HealthCheckResult.md
docs/HuntingDog.md
docs/ImportTestDatetimeApi.md
docs/Info.md
docs/InnerDictWithProperty.md
Expand Down Expand Up @@ -180,6 +181,7 @@ petstore_api/models/foo_get_default_response.py
petstore_api/models/format_test.py
petstore_api/models/has_only_read_only.py
petstore_api/models/health_check_result.py
petstore_api/models/hunting_dog.py
petstore_api/models/info.py
petstore_api/models/inner_dict_with_property.py
petstore_api/models/input_all_of.py
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ Class | Method | HTTP request | Description
- [FormatTest](docs/FormatTest.md)
- [HasOnlyReadOnly](docs/HasOnlyReadOnly.md)
- [HealthCheckResult](docs/HealthCheckResult.md)
- [HuntingDog](docs/HuntingDog.md)
- [Info](docs/Info.md)
- [InnerDictWithProperty](docs/InnerDictWithProperty.md)
- [InputAllOf](docs/InputAllOf.md)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# HuntingDog


## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**is_trained** | **bool** | | [optional]

## Example

```python
from petstore_api.models.hunting_dog import HuntingDog

# TODO update the JSON string below
json = "{}"
# create an instance of HuntingDog from a JSON string
hunting_dog_instance = HuntingDog.from_json(json)
# print the JSON string representation of the object
print HuntingDog.to_json()

# convert the object into a dict
hunting_dog_dict = hunting_dog_instance.to_dict()
# create an instance of HuntingDog from a dict
hunting_dog_from_dict = HuntingDog.from_dict(hunting_dog_dict)
```
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)


Loading

0 comments on commit 7a23ac7

Please sign in to comment.