diff --git a/datamodel_code_generator/model/base.py b/datamodel_code_generator/model/base.py index 63c97ab5a..f95a7bf42 100644 --- a/datamodel_code_generator/model/base.py +++ b/datamodel_code_generator/model/base.py @@ -4,7 +4,7 @@ from collections import defaultdict from functools import wraps from pathlib import Path -from typing import Any, Callable, DefaultDict, Dict, List, Optional, Set +from typing import Any, Callable, DefaultDict, Dict, List, Optional, Set, Union from jinja2 import Environment, FileSystemLoader, Template from pydantic import BaseModel, root_validator @@ -108,6 +108,10 @@ def __init__(self, **values: Any) -> None: self.imports.extend(data_type.imports_) self.type_hint = self._get_type_hint() + @property + def represented_default(self) -> str: + return repr(self.default) + class TemplateBase(ABC): def __init__(self, template_file_path: Path) -> None: diff --git a/datamodel_code_generator/model/pydantic/base_model.py b/datamodel_code_generator/model/pydantic/base_model.py index f6da44bae..62f27184f 100644 --- a/datamodel_code_generator/model/pydantic/base_model.py +++ b/datamodel_code_generator/model/pydantic/base_model.py @@ -1,8 +1,6 @@ from pathlib import Path from typing import Any, DefaultDict, Dict, List, Optional, Set, Union -from jinja2 import Environment, FileSystemLoader, Template - from datamodel_code_generator.imports import Import from datamodel_code_generator.model import DataModel, DataModelFieldBase from datamodel_code_generator.model.pydantic.types import get_data_type @@ -12,18 +10,6 @@ class DataModelField(DataModelFieldBase): _FIELDS_KEYS: Set[str] = {'alias', 'example', 'examples', 'description', 'title'} - def get_valid_argument(self, value: Any) -> Union[str, List[Any], Dict[Any, Any]]: - if isinstance(value, str): - return repr(value) - elif isinstance(value, list): - return [self.get_valid_argument(i) for i in value] - elif isinstance(value, dict): - return { - self.get_valid_argument(k): self.get_valid_argument(v) - for k, v in value.items() - } - return value - def __init__(self, **values: Any) -> None: super().__init__(**values) @@ -52,14 +38,14 @@ def field(self) -> Optional[str]: def __str__(self) -> str: field_arguments = [ - f"{k}={self.get_valid_argument(v)}" + f"{k}={repr(v)}" for k, v in self.dict(include=self._FIELDS_KEYS).items() if v is not None ] if not field_arguments: return "" - value_arg = "..." if self.required else self.get_valid_argument(self.default) + value_arg = "..." if self.required else repr(self.default) kwargs = ",".join(field_arguments) return f'Field({value_arg}, {kwargs})' diff --git a/datamodel_code_generator/model/template/pydantic/BaseModel.jinja2 b/datamodel_code_generator/model/template/pydantic/BaseModel.jinja2 index 46b07408e..e800d99bb 100644 --- a/datamodel_code_generator/model/template/pydantic/BaseModel.jinja2 +++ b/datamodel_code_generator/model/template/pydantic/BaseModel.jinja2 @@ -14,7 +14,7 @@ class {{ class_name }}({{ base_class }}): {%- elif field.required %} {{ field.name }}: {{ field.type_hint }} {%- else %} - {{ field.name }}: {{ field.type_hint }} = {{ field.default }} + {{ field.name }}: {{ field.type_hint }} = {{ field.represented_default }} {%- endif %} {%- for method in methods -%} {{ method }} diff --git a/datamodel_code_generator/parser/jsonschema.py b/datamodel_code_generator/parser/jsonschema.py index 37922f0a3..eb84fae30 100644 --- a/datamodel_code_generator/parser/jsonschema.py +++ b/datamodel_code_generator/parser/jsonschema.py @@ -120,29 +120,6 @@ def validate_items(cls, values: Any) -> Any: return None return values - @property - def typed_default(self) -> Union[str, bool, Dict[Any, Any], None]: - if self.default is not None and self.type: - type_ = self.type[0] if isinstance(self.type, list) else self.type - return self._get_typed_default(type_, self.default) - return None - - @classmethod - def _get_typed_default( - cls, type_: str, default_value: Any - ) -> Union[str, bool, Dict[Any, Any], None]: - if type_ == 'integer' or type_ == 'number': - return default_value - elif type_ == 'boolean': - if default_value == 'true': - return True - return False - elif type_ == 'object' and isinstance(default_value, dict): - return default_value - elif type_ == 'null': - return None - return f"'{default_value}'" - JsonSchemaObject.update_forward_refs() @@ -371,7 +348,7 @@ def parse_object_fields(self, obj: JsonSchemaObject) -> List[DataModelFieldBase] name=field_name, example=field.examples, description=field.description, - default=field.typed_default, + default=field.default, title=field.title, data_types=field_types, required=required, @@ -441,7 +418,7 @@ def parse_array_fields( field = self.data_model_field_type( data_types=item_obj_data_types, example=obj.examples, - default=obj.typed_default, + default=obj.default, description=obj.description, title=obj.title, required=True, @@ -486,7 +463,7 @@ def parse_root_type(self, name: str, obj: JsonSchemaObject) -> None: data_types=types, description=obj.description, example=obj.examples, - default=obj.typed_default, + default=obj.default, required=not obj.nullable, ) ], diff --git a/tests/model/pydantic/test_base_model.py b/tests/model/pydantic/test_base_model.py index 3ca47d9d2..176ca768d 100644 --- a/tests/model/pydantic/test_base_model.py +++ b/tests/model/pydantic/test_base_model.py @@ -17,7 +17,7 @@ def test_base_model(): def test_base_model_optional(): field = DataModelField( - name='a', data_types=[DataType(type='str')], default="'abc'", required=False + name='a', data_types=[DataType(type='str')], default='abc', required=False ) base_model = BaseModel(name='test_model', fields=[field]) @@ -33,7 +33,7 @@ def test_base_model_optional(): def test_base_model_decorator(): field = DataModelField( - name='a', data_types=[DataType(type='str')], default="'abc'", required=False + name='a', data_types=[DataType(type='str')], default='abc', required=False ) base_model = BaseModel( @@ -121,7 +121,7 @@ def test_base_model_reserved_name(): ({'examples': [1, 2, 3]}, "Field(None, examples=[1, 2, 3])"), ( {'examples': {'name': 'dog', 'age': 1}}, - 'Field(None, examples={"\'name\'": "\'dog\'", "\'age\'": 1})', + 'Field(None, examples={\'name\': \'dog\', \'age\': 1})', ), ({'default': 'abc', 'title': 'title'}, 'Field(\'abc\', title=\'title\')'), ({'default': 123, 'title': 'title'}, 'Field(123, title=\'title\')'), diff --git a/tests/parser/test_jsonschema.py b/tests/parser/test_jsonschema.py index ed88efbe7..62e4af0fb 100644 --- a/tests/parser/test_jsonschema.py +++ b/tests/parser/test_jsonschema.py @@ -5,7 +5,6 @@ import pytest from datamodel_code_generator import DataModelField -from datamodel_code_generator.model import DataModelFieldBase from datamodel_code_generator.model.pydantic import BaseModel, CustomRootType from datamodel_code_generator.parser.base import dump_templates from datamodel_code_generator.parser.jsonschema import ( @@ -189,17 +188,46 @@ def test_parse_one_of_object(source_obj, generated_classes): @pytest.mark.parametrize( - 'type_,default,expected', + 'source_obj,generated_classes', [ - ('string', 'abc', "'abc'"), - ('string', '', "''"), - ('number', 123, 123), - ('number', 0, 0), - ('boolean', 'true', True), - ('boolean', 'false', False), - ('null', 'null', None), - ('object', {'abc': 123, 'efg': 'hij'}, {'abc': 123, 'efg': 'hij'}), + ( + { + "$id": "https://example.com/person.schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "defaults", + "type": "object", + "properties": { + "string": {"type": "string", "default": "default string",}, + "string_on_field": { + "type": "string", + "default": "default string", + "description": "description", + }, + "number": {"type": "number", "default": 123}, + "number_on_field": { + "type": "number", + "default": 123, + "description": "description", + }, + "number_array": {"type": "array", "default": [1, 2, 3]}, + "string_array": {"type": "array", "default": ["a", "b", "c"]}, + "object": {"type": "object", "default": {"key": "value"}}, + }, + }, + """class Defaults(BaseModel): + string: Optional[str] = 'default string' + string_on_field: Optional[str] = Field('default string', description='description') + number: Optional[float] = 123 + number_on_field: Optional[float] = Field(123, description='description') + number_array: Optional[List] = [1, 2, 3] + string_array: Optional[List] = ['a', 'b', 'c'] + object: Optional[Dict[str, Any]] = {'key': 'value'}""", + ) ], ) -def test_typed_default(type_, default, expected): - assert JsonSchemaObject(type=type_, default=default).typed_default == expected +def test_parse_default(source_obj, generated_classes): + parser = JsonSchemaParser( + BaseModel, CustomRootType, data_model_field_type=DataModelField + ) + parser.parse_raw_obj('Defaults', source_obj) + assert dump_templates(list(parser.results)) == generated_classes