diff --git a/dataclasses_avroschema/model_generator/lang/python/base.py b/dataclasses_avroschema/model_generator/lang/python/base.py index 48903801..2c59d124 100644 --- a/dataclasses_avroschema/model_generator/lang/python/base.py +++ b/dataclasses_avroschema/model_generator/lang/python/base.py @@ -327,6 +327,9 @@ def parse_logical_type(self, *, field: JsonDict) -> str: elif logical_type == field_utils.DECIMAL: # this is a special case for logical types type = self.parse_decimal(field=field, default=default) + elif logical_type not in self.logical_types_imports: + # Then it is a custom logicalType, so we default to the native type + type = self.get_language_type(type=field["type"]) else: # add the logical type import self.imports.add(self.logical_types_imports[logical_type]) diff --git a/docs/logical_types.md b/docs/logical_types.md index 72709361..c3d26ab3 100644 --- a/docs/logical_types.md +++ b/docs/logical_types.md @@ -13,6 +13,9 @@ The following list represent the avro logical types mapped to python types: | string | uuid | uuid.UUID | | bytes | decimal | types.condecimal | +!!! note + Custom `logicalTypes` are not supported + ## Date ```python title="Date example" diff --git a/docs/model_generator.md b/docs/model_generator.md index 58fc72a2..8e80d913 100644 --- a/docs/model_generator.md +++ b/docs/model_generator.md @@ -123,6 +123,46 @@ class Address(AvroModel): Generating a single module from multiple schemas is useful for example to group schemas that belong to the same namespace. +## LogicalTypes + +Native `logicalTypes` are supported by `dataclasses-avroschema` but custom ones are not. If you defined a custom `logicalType` then +the fallback is used when generating the field. In the next example we have a `logicalType` defined as `url`, which is not a native one, +then the model generated will use `string` + +```python +from dataclasses_avroschema import ModelGenerator, ModelType + +model_generator = ModelGenerator() + + +schema = { + "type": "record", + "name": "TestEvent", + "fields": [ + { + "name": "regular", + "type": { + "type": "string", + "logicalType": "url" + }, + "doc": "Urls" + } + ], +} + +print(model_generator.render(schema=schema)) + +""" +from dataclasses_avroschema import AvroModel +import dataclasses + + +@dataclasses.dataclass +class TestEvent(AvroModel): + regular: str = dataclasses.field(metadata={'doc': 'Urls'}) +""" +``` + ## Render Pydantic models It is also possible to render `BaseModel` (pydantic) and `AvroBaseModel` (avro + pydantic) models as well. diff --git a/tests/model_generator/conftest.py b/tests/model_generator/conftest.py index ff3800db..f494f192 100644 --- a/tests/model_generator/conftest.py +++ b/tests/model_generator/conftest.py @@ -759,6 +759,26 @@ def schema_with_logical_types_field_order() -> JsonDict: } +@pytest.fixture +def schema_with_unknown_logical_types() -> JsonDict: + return { + "type": "record", + "name": "TestEvent", + "namespace": "com.example", + "fields": [ + {"name": "occurredAt", "type": {"type": "long", "logicalType": "timestamp-millis"}, "doc": "Event time"}, + { + "name": "previous", + "type": { + "type": "record", + "name": "urls", + "fields": [{"name": "regular", "type": {"type": "string", "logicalType": "url"}, "doc": "Urls"}], + }, + }, + ], + } + + @pytest.fixture def schema_with_pydantic_fields() -> JsonDict: return { diff --git a/tests/model_generator/test_model_generator.py b/tests/model_generator/test_model_generator.py index c59fdea7..3c3d29bd 100644 --- a/tests/model_generator/test_model_generator.py +++ b/tests/model_generator/test_model_generator.py @@ -594,6 +594,36 @@ class LogicalTypes(AvroModel): assert result.strip() == expected_result.strip() +def test_schema_with_unknown_logical_types(schema_with_unknown_logical_types: types.JsonDict) -> None: + expected_result = """ +from dataclasses_avroschema import AvroModel +import dataclasses +import datetime + + +@dataclasses.dataclass +class Urls(AvroModel): + regular: str = dataclasses.field(metadata={'doc': 'Urls'}) + + + class Meta: + schema_name = "urls" + +@dataclasses.dataclass +class TestEvent(AvroModel): + occurredAt: datetime.datetime = dataclasses.field(metadata={'doc': 'Event time'}) + previous: Urls + + + class Meta: + namespace = "com.example" + +""" + model_generator = ModelGenerator() + result = model_generator.render(schema=schema_with_unknown_logical_types) + assert result.strip() == expected_result.strip() + + def test_field_order(schema_with_logical_types_field_order: types.JsonDict) -> None: release_datetime = render_datetime(value=1570903062000, format=field_utils.TIMESTAMP_MILLIS) release_datetime_micro = render_datetime(value=1570903062000000, format=field_utils.TIMESTAMP_MICROS)