Skip to content
This repository has been archived by the owner on Apr 10, 2023. It is now read-only.

Model tagging when serializing and deserializing #81

Closed
rossmacarthur opened this issue Mar 8, 2019 · 1 comment
Closed

Model tagging when serializing and deserializing #81

rossmacarthur opened this issue Mar 8, 2019 · 1 comment
Labels
feature New feature or request

Comments

@rossmacarthur
Copy link
Owner

rossmacarthur commented Mar 8, 2019

It is very common to want to have an abstract Model that can will be automatically deserialized into the correct subclass.

Example

from serde import Model, fields

class Abstract(Model):
    x = fields.Str()

class A(Abstract):
    a = fields.Int()

class B(Abstract):
    b = fields.Float()


a = Abstract.from_dict({
    'x': 'test',
    'a': 5
}) 

b = Abstract.from_dict({
    'x': 'test',
    'b': 5.0
}) 

The current easiest way to do this is through tagging, and then overriding the from_dict and to_dict methods of the Abstract model to serialize and deserialize the tag field.

class Abstract(Model):
    x = fields.Str()

    @classmethod
    def from_dict(cls, d, **kwargs):
        if cls is Abstract:  # this is the key part that makes this all work
            tag = d.pop('tag')
            subcls = {c.__name__: c for c in cls.__subclasses__()}[tag]
            return subcls.from_dict(d, **kwargs)

        return super(Abstract, cls).from_dict(d, **kwargs)

    def to_dict(self, **kwargs):
        d = self.to_dict(**kwargs)
        d['tag'] = self.__class__.__name__
        return d

You can even create a subclass-able Tagged Model.

class Tagged(Model):

    @classmethod
    def from_dict(cls, d, **kwargs):
        if Tagged in cls.__bases__:  # this is the key part that makes this all work
            tag = d.pop('tag')
            subcls = {c.__name__: c for c in cls.__subclasses__()}[tag]
            return subcls.from_dict(d, **kwargs)

        return super(Tagged, cls).from_dict(d, **kwargs)

    def to_dict(self, **kwargs):
        d = self.to_dict(**kwargs)
        d['tag'] = self.__class__.__name__
        return d

But it might be better to do this with decorators? We would also want to have the tag field be configurable. Maybe this is the time to add a Meta inner class, that can be used by the ModelType metaclass to adjust Model settings.

Perhaps we should move away from metaclasses completely and use decorators like attrs.

@rossmacarthur rossmacarthur added the feature New feature or request label Mar 8, 2019
@rossmacarthur
Copy link
Owner Author

rossmacarthur commented Mar 12, 2019

I think we should add a Meta class

class Abstract(Model):
    class Meta:
         tag = 'kind'

However, there are multiple ways of tagging so the options should reflect them

Internally tagged

{'kind': 'A', 'field_x': 'value', 'field_y': 'value', ... }`

Externally tagged

{'A': { field_x': 'value', 'field_y': 'value', ... }}

Adjacently tagged

{'kind': 'A', 'data': { 'field_x': 'value', 'field_y': 'value', ... }}

Untagged

Figured out based on the fields, first subclass that deserializes is used

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
feature New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant