marshmallow-sqlalchemy icon indicating copy to clipboard operation
marshmallow-sqlalchemy copied to clipboard

`AttributeError: 'dict' object has no attribute '_sa_instance_state'` when nesting object

Open melchoir55 opened this issue 6 years ago • 4 comments

I am trying to deserialize a deep structure with marshmallow. For example:

    hour = {
        'day': {
            'name': 'monday'
        }
    }
    loaded_hour, error = HoursSerializationSchema().load(hour) # this works

    new_practitioner_at_location = {
        'hours': [
            hour
        ]
    }
    loaded, error = PractitionerToServiceLocationSerializationSchema().load(new_practitioner_at_location) # this fails
   

When I run try to deserialize the new_practitioner_at_location I get the following (occurs when the serializer is working on the 'day' key):

AttributeError: 'dict' object has no attribute '_sa_instance_state'

self-contained script showing the problem:


    from sqlalchemy import Column, Integer, ForeignKey, String
    from sqlalchemy.orm import relationship, backref
    from sqlalchemy.ext.declarative import declarative_base
    import os
    from flask import Flask
    from flask_sqlalchemy import SQLAlchemy
    from flask_marshmallow import Marshmallow
    from flask_migrate import Migrate
    
    base = declarative_base()
    
    
    class HoursDay(base):
        __tablename__ = 'HoursDay'
        uid = Column(Integer, primary_key=True)
    
        name = Column(String)
    
        hour_id = Column(Integer, ForeignKey('Hours.uid'))
        hour = relationship("Hours", back_populates="day")
    
        def __init__(self, **kwargs):
            super().__init__(**kwargs)
    
    
    class Hours(base):
        __tablename__ = 'Hours'
        uid = Column(Integer, primary_key=True)
    
        practitioner_at_location_id = Column(Integer, ForeignKey('PractitionerToServiceLocation.uid'))
        practitioner_at_location = relationship('PractitionerToServiceLocation', back_populates="hours")
    
        day = relationship(HoursDay, uselist=False, back_populates="hour")
    
        def __repr__(self):
            return f'<Hours {self.uid}>'
    
    
    class PractitionerToServiceLocation(base):
        """
        A practitioner practices at a number of service locations.
        """
        __tablename__ = 'PractitionerToServiceLocation'
        uid = Column(Integer, primary_key=True)
    
        hours = relationship("Hours", back_populates="practitioner_at_location")
    
        def __init__(self, **kwargs):
            super().__init__(**kwargs)
    
        def __repr__(self):
            return f'<PractitionerToServiceLocation {self.uid}>'
    
    
    app = Flask(__name__)
    app.config.from_object(os.environ['APP_SETTINGS'])
    db = SQLAlchemy(app, model_class=base)
    ma = Marshmallow(app)
    migrate = Migrate(app, db)
    
    from marshmallow import fields
    
    
    class HoursDaySerializationSchema(ma.ModelSchema):
        class Meta:
            model = HoursDay
    
    
    class HoursSerializationSchema(ma.ModelSchema):
        class Meta:
            model = Hours
    
        day = fields.Nested(HoursDaySerializationSchema)
    
    
    class PractitionerToServiceLocationSerializationSchema(ma.ModelSchema):
        class Meta:
            model = PractitionerToServiceLocation
    
            hours = fields.Nested('HoursSerializationSchema', many=True)
    
    
    if __name__ == "__main__":
        hour = {
            'day': {
                'name': 'monday'
            }
        }
        loaded_hour, error = HoursSerializationSchema().load(hour) # this works
    
        new_practitioner_at_location = {
            'hours': [
                hour
            ]
        }
        loaded, error = PractitionerToServiceLocationSerializationSchema().load(new_practitioner_at_location) # this fails
        print('hi')


Link to SO post: https://stackoverflow.com/questions/55129663/marshmallow-deserializing-fails-when-structure-is-nested

melchoir55 avatar Mar 12 '19 20:03 melchoir55

Could someone take a quick look at this, please? I think it exposes a bug in marshmallow-sqlalchemy. Maybe @sloria ? This script can be copy/pasted and run pretty easily.

melchoir55 avatar Mar 17 '19 02:03 melchoir55

I think that what is happening is that marshmallow is not attempting to deserialize the HoursDay object when trying to deserialize the new_practitioner_at_location dict. If I remove the backpopulates behavior from the HoursDay.hour field then you can see it just assign the unserialized data struct to the field. This makes no sense at all to me, especially since it works when you just deserialize the hour dict directly instead of embedding it inside new_practitioner_at_location.

Any help would be appreciated.

melchoir55 avatar Mar 19 '19 20:03 melchoir55

Sorry, I'm quite busy this week so can't look into this. Thank you for investigating.. I'd welcome a PR if you arrive at a fix, or even just an analysis of the problem.

sloria avatar Mar 19 '19 21:03 sloria

The issue can be closed. As stated in SO post, an extra space caused the hour field not being declared in the object.

eino avatar Dec 13 '19 12:12 eino