首页 > 解决方案 > Marshmallow ModelSchema to Mapping Class Inheritance SqlAlchemy

问题描述

i am with this problem, i using ModelSchema to validade my API input and this give me a Database Model, it works fine, but if i use a Mapping Class Inheritance(Single Table type) the ModelSchema give me only the parent without the children attributes.

class SerialInterface(sql.Model):
    type = sql.Column(sql.String(50))
    polymorphic_identity = {
        'polymorphic_identity': 'serial_interface',
        'polymorphic_on': type,
    }

    id = sql.Column(types.Integer, primary_key=True)
    transmission_rate = sql.Column(types.Integer)

    mad_id = sql.Column(types.Integer, sql.ForeignKey('mad.id'))

    serial_protocol = sql.relationship(SerialProtocol, uselist=False, cascade="all, delete-orphan")

class RS485(SerialInterface):
    parity = sql.Column(EnumSmart(Parity))
    data_bits = sql.Column(types.Integer)
    stop_bits = sql.Column(types.Integer)

    polymorphic_identity = SerialTypes.RS485.name

class SerialInterfaceForm(BaseSchema):
    class Meta(BaseSchema.Meta):
        model = SerialInterface

    transmission_rate = Integer(required=True)
    mad_id = Integer(required=True)
    serial_protocol = Nested(SerialProtocolForm, required=True)

    parity = EnumField(Parity, required=True)
    data_bits = Integer(required=True)
    stop_bits = Integer(required=True)

When i use SerialInterfaceForm().load(input_json) it return <SerialInterface (transient 1477443543280)> , using __dict__: {'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x00000157FE7F6550>, 'type': 'RS485', 'transmission_rate': 9600, 'mad_id': 1, 'serial_protocol': <SerialProtocol (transient 1477443544120)>} My doubt is how i can make the Marshmallow ModelSchema return the correct child with his attributes

标签: pythonflasksqlalchemyflask-sqlalchemyflask-marshmallow

解决方案


我找到了解决方案,覆盖了ModelSchema的make_instance,并调用__mapper__.polymorphic_map了找到的子类

class BaseSchema(marshmallow.ModelSchema):

    @post_load
    def make_instance(self, data, **kwargs):
        """Deserialize data to an instance of the model. Update an existing row
        if specified in `self.instance` or loaded by primary key(s) in the data;
        else create a new row.

        :param data: Data to deserialize.
        """

        instance = self.instance or self.get_instance(data)
        if instance is not None:
            for key, value in iteritems(data):
                setattr(instance, key, value)
            return instance
        print(self.opts.model)
        # kwargs, association_attrs = self._split_model_kwargs_association(data)
        try:
            instance = self.opts.model.__mapper__.polymorphic_map[data['type']].class_(**kwargs)
        except Exception:
            instance = self.opts.model(**kwargs)

        for attr, value in iteritems(data):
            setattr(instance, attr, value)
        return instance

    class Meta:
        sqla_session = sql.session

推荐阅读