Десериализация Marshmallow завершается неудачей, когда структура вложена

#python #flask #sqlalchemy #marshmallow

#python #flask #sqlalchemy #marshmallow

Вопрос:

Я пытаюсь десериализовать глубокую структуру с помощью marshmallow. Например:

 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
  

Когда я пытаюсь десериализовать new_practitioner_at_location , я получаю следующее (происходит, когда сериализатор работает с ключом ‘day’):

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

Обратите внимание, что та же схема работает для десериализации той же структуры данных (hour), когда эта структура не вложена внутри new_practitioner_at_location .

автономный скрипт, показывающий проблему:

 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')
  

Обновить:

Я думаю, что происходит то, что marshmallow не пытается десериализовать HoursDay объект при попытке десериализовать new_practitioner_at_location dict. Если я удалю backpopulates поведение из HoursDay.hour поля, вы сможете увидеть, что это просто присваивает полю несериализованную структуру данных. Для меня это вообще не имеет смысла, тем более что это работает, когда вы просто десериализуете hour dict напрямую, вместо того, чтобы встраивать его внутрь new_practitioner_at_location . Любая помощь была бы оценена.

Комментарии:

1. К вашему сведению, ваш автономный скрипт не запускается как есть, поскольку он пытается загрузить конфигурацию из env var и не создает таблицы db.

Ответ №1:

Это простая ошибка, похожая на опечатку:

 class PractitionerToServiceLocationSerializationSchema(ma.ModelSchema):
    class Meta:
        model = PractitionerToServiceLocation

        hours = fields.Nested('HoursSerializationSchema', many=True)
  

Вы определяете hours внутри class Meta , но это должно быть в самой вашей схеме:

 class PractitionerToServiceLocationSerializationSchema(ma.ModelSchema):
    class Meta:
        model = PractitionerToServiceLocation

    hours = fields.Nested('HoursSerializationSchema', many=True)
  

Комментарии:

1. Вау. Вы наблюдаете один из самых сложных фейспалмов, которые я когда-либо делал.