Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support custom config for dacite defaults in AvroModel #242

Closed
billy-doyle opened this issue Feb 9, 2023 · 2 comments
Closed

Support custom config for dacite defaults in AvroModel #242

billy-doyle opened this issue Feb 9, 2023 · 2 comments

Comments

@billy-doyle
Copy link
Contributor

Is your feature request related to a problem? Please describe.
Problem is I have a dataclass which has a Union of multiple similar but slightly different schemas. One such schema has all the fields from another, but the other has an additional field. By suppling the strict and strict unions match parameters I am able fix this.

Full reproducible example below:

from typing import Type, TypeVar, Union
from dataclasses import dataclass
from dacite import from_dict
from dataclasses_avroschema import AvroModel
from dataclasses_avroschema.types import JsonDict


@dataclass
class Foo(AvroModel):
    b: int

@dataclass
class Bar(AvroModel):
    a: str
    b: int


@dataclass
class Baz(AvroModel):
    action: Union[Foo, Bar]


data = {"a": "a", "b": 2}
bar = from_dict(Bar, data)
print(bar)
# Bar(a='a', b=2)

baz = Baz(bar)
print(baz)
# Baz(action=Bar(a='a', b=2))

serialized_val = Baz(bar).serialize()

print(Baz.deserialize(serialized_val, create_instance=False))
# {'action': {'a': 'a', 'b': 2}}
print(Baz.deserialize(serialized_val))
# Baz(action=Foo(b=2)) # INCORRECT!



CT = TypeVar("CT", bound="AvroModel")

class CustomAvroModel(AvroModel):
    # https://github.com/marcosschroh/dataclasses-avroschema/blob/2a100666c93afb3f4916ea84b2ed9904b71a3632/dataclasses_avroschema/schema_generator.py#L206

    @classmethod
    def config(cls: Type[CT]) -> JsonDict:
        """
        Get the default config for dacite and always include the self reference
        """
        # We need to make sure that the `avro schemas` has been generated, otherwise cls.klass is empty
        # It won't affect the performance because the rendered schema will be store in cls.rendered_schema
        if cls.klass is None:
            # Generate dataclass and metadata
            cls.klass = cls.generate_dataclass()

        return {
            "check_types": False,
            "forward_references": {
                cls.klass.__name__: cls.klass,
            },
            "strict_unions_match": True,
            "strict": True,
        }


@dataclass
class Baz(CustomAvroModel):
    action: Union[Foo, Bar]


baz = Baz(bar)
serialized_val = Baz(bar).serialize()
print(Baz.deserialize(serialized_val))
# Baz(action=Bar(a='a', b=2)) # CORRECT!

Describe the solution you'd like
I would like a simpler way to specify the dacite defaults passed to the config method. I am not sure the best way to handle this, however maybe something like the following would be better?:

@dataclass
class Baz(AvroModel):
    action: Union[Foo, Bar]

    class DaciteConfig:
        strict = True
        strict_unions_match = True

Describe alternatives you've considered
Alternative is to have the CustomAvroModel as currently shown in the reproducible example. This could likely be abstracted better.

Additional context
No additional context.

@marcosschroh
Copy link
Owner

marcosschroh commented Feb 20, 2023

Hi @billy-doyle

I think we can add this functionality to the class Meta. I will create a PR

@billy-doyle
Copy link
Contributor Author

I had considered putting it in the Meta class but wasn't sure if it was the proper place or not. Sounds good to me though!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants