akushyn
akushyn

Reputation: 103

Include child field in marshmallow schema in polymorphic identity

I have a model in database to store global app setting. Settings can be with different type, so I've made a data_type ad discriminator with polymorphic identities.

    class MiscSetting(db.Model):
    __tablename__ = "settings_misc"
    __table_args__ = (db.UniqueConstraint("county", "name", name="uc_county_name"),)

    id = db.Column(db.Integer, primary_key=True)
    county = db.Column(db.String, nullable=True)
    name = db.Column(db.String(length=50))
    data_type = db.Column(db.String(50))

    __mapper_args__ = {
        'polymorphic_on': data_type,
        'with_polymorphic': '*'
    }


    class StringSetting(MiscSetting):
    __tablename__ = 'settings_string'
    id = db.Column(db.Integer, db.ForeignKey('settings_misc.id'), primary_key=True)
    value = db.Column(db.String)
    __mapper_args__ = {
        'polymorphic_identity': 'string'
    }


    class IntegerSetting(MiscSetting):
    __tablename__ = 'settings_integer'
    id = db.Column(db.Integer, db.ForeignKey('settings_misc.id'), primary_key=True)
    value = db.Column(db.Integer)
    __mapper_args__ = {
        'polymorphic_identity': 'integer'
    }

When I try to dump all objects I received, not "value" found.

    settings = db.session.query(MiscSetting).all()
    dump = json.dumps(MiscSettingSchema().dump(settings, many=True))

    class MiscSettingSchema(BaseSchema):
    class Meta:
        model = MiscSetting

The question is how to include value into Marshmallow Schema?

Upvotes: 1

Views: 568

Answers (1)

datasmith
datasmith

Reputation: 764

I'm not using marshmallow-sqlalchemy but I just ran into polymorphic relationships in plain marshmallow and here's how I solved the problem using a custom field.

from marshmallow import Schema
from marshmallow.fields import (
    String,
    Field,
)
from marshmallow.exceptions import ValidationError

class ClassA(Schema):
    attribute_1 = String(default='Hello Polymorphic Relationships!')
    attribute_2 = String(default='Hello Polymorphic Relationships!')

class ClassB(Schema):
    attribute_3 = String(default='Hello Polymorphic Associations!')
    attribute_4 = String(default='Hello Polymorphic Associations!')


class PolymorphicRelationship(Field):

    def __init__(self, *args, nested=(), **kwargs):
        super().__init__(*args, **kwargs)
        self.CLASSES = nested

    def _serialize(self, value, attr, obj, **kwargs):
        for nested_class in self.CLASSES:
            try:
                return nested_class().dump(value)
            except ValidationError:
                continue
        raise ValidationError(f'Unable to validate with: {self.CLASSES}')


    def _deserialize(self, value, attr, data, **kwargs):
        for nested_class in self.CLASSES:
            try:
                return nested_class().load(value)
            except ValidationError:
                continue
        raise ValidationError(f'Unable to validate with: {self.CLASSES}')

class ClassC(Schema):
    data_type = PolymorphicRelationship(nested=(ClassA, ClassB))


def main():
    schema_a = ClassA()
    schema_b = ClassB()
    schema_c = ClassC()
    a = schema_a.load({'attribute_1': 'foo'})
    b = schema_b.load({'attribute_3': 'bar'})
    c = schema_c.load({'data_type': {'attribute_3': 'foo'}})
    d = schema_c.load({'data_type': {'attribute_4': 'bar'}})
    print('a', schema_a.dump(a))
    print('b', schema_a.dump(b))
    print('c', c)
    print('d', d)


if __name__ == '__main__':
    main()

Upvotes: 1

Related Questions