marshmallow-sqlalchemy
marshmallow-sqlalchemy copied to clipboard
`AttributeError: 'dict' object has no attribute '_sa_instance_state'` when nesting object
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
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.
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.
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.
The issue can be closed. As stated in SO post, an extra space caused the hour field not being declared in the object.