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 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'
Note that the same schema works to deserialize the same data structure (hour) when that structure is not nested inside the new_practitioner_at_location
.
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')
Update:
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.
This is a simple typo-like bug:
class PractitionerToServiceLocationSerializationSchema(ma.ModelSchema):
class Meta:
model = PractitionerToServiceLocation
hours = fields.Nested('HoursSerializationSchema', many=True)
You are defining hours
inside class Meta
, but it needs to be in your schema itself:
class PractitionerToServiceLocationSerializationSchema(ma.ModelSchema):
class Meta:
model = PractitionerToServiceLocation
hours = fields.Nested('HoursSerializationSchema', many=True)