Вопрос:

Загрузка / десериализация объекта с вложенным полем

python sqlalchemy marshmallow

26 просмотра

1 ответ

51 Репутация автора

У меня есть две sqlalchemyмодели - ServiceRecordи Service, с many-to-manyотношениями, которые решаются с помощью service_record_servicesтаблицы. Я пытаюсь десериализовать ServiceRecordобъект из следующего JSON и загрузить Serviceобъекты из БД через idsпредоставленный. Вместо этого я получаю новые Serviceобъекты.

# from POST
{'service_date': '08/21/2019 08:00 pm', 'site_id': 33, 'subcontractor_id': 37, 'services': [{'id':1},{'id': 2}]}

Модели:

class Service(db.Model):
    __tablename__ = 'services'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False, unique=True)
    comments = db.Column(db.String(200), nullable=True, unique=False)

class ServiceRecord(db.Model):
    __tablename__ = 'service_records'
    id = db.Column(db.Integer, primary_key=True)
    created = db.Column(db.DateTime, server_default=db.func.now())
    datetime = db.Column(db.DateTime)
    subcontractor_id = db.Column(db.Integer, db.ForeignKey('subcontractors.id'))
    subcontractor = db.relationship( 'Subcontractor', back_populates='service_records')
    site_id = db.Column(db.Integer, db.ForeignKey('sites.id'))
    site = db.relationship('Site', back_populates='service_records')
    # all services for this record
    services = db.relationship('Service', secondary=lambda: service_record_services)

Schemas:

class ServiceSchema(ma.ModelSchema):
    class Meta:
        include_fk = True
        model = models.Service
        strict = False
        sqla_session = db.session
        transient = True

    name = fields.Str(dump_only=True)

class ServiceRecordSchema(ma.ModelSchema):
    class Meta:
        include_fk = True
        model = models.ServiceRecord
        sqla_session = db.session

    id = fields.Int(dump_only=True)
    subcontractor_name = fields.Function(lambda obj: obj.subcontractor.name, dump_only=True)
    site_name = fields.Function(lambda obj: obj.site.name, dump_only=True)
    services = fields.Nested(ServiceSchema, many=True)
    service_date = fields.DateTime(format='%m/%d/%Y %I:%M %p', attribute='datetime')

    @pre_load
    def process_services(self, data):
        current_app.logger.debug('[pre_load - 1] data: {}'.format(data))
        service_ids = map(lambda x: x.get('id'), data.get('services'))
        services = models.Service.query.filter(models.Service.id.in_(service_ids)).all()
        # i'd like to be able to use these Service objects in the loaded ServiceRecord
        data['_services'] = services
        return data
DEBUG in schema: [pre_load - 1] data: {'service_date': '08/21/2019 08:00 pm', 'site_id': 33, 'subcontractor_id': 37, 'services': [{'id': 1}, {'id': 2}]}
 DEBUG in schema: service_ids: [1, 2]
 DEBUG in resource: resource: <class 'app.models.ServiceRecord'>(site_id:33, subcontractor_id:37, datetime:2019-08-21 20:00:00)
 DEBUG in resource: resource services: [<Service(name=None)>, <Service(name=None)>]
Автор: Alex Источник Размещён: 09.08.2019 03:21

Ответы (1)


0 плюса

51 Репутация автора

Оказывается, все, что мне нужно было сделать, это добавить оба обязательных поля в мою ServiceSchema, удалить transient=Trueи все. Вот рабочий код:

class ServiceSchema(ma.ModelSchema):
    class Meta:
        include_fk = True
        model = models.Service
        strict = False
        sqla_session = db.session

    id = fields.Int()
    name = fields.Str()

class ServiceRecordSchema(ma.ModelSchema):
    class Meta:
        include_fk = True
        model = models.ServiceRecord
        sqla_session = db.session

    id = fields.Int(dump_only=True)
    subcontractor_name = fields.Function(lambda obj: obj.subcontractor.name, dump_only=True)
    site_name = fields.Function(lambda obj: obj.site.name, dump_only=True)
    service_date = fields.DateTime(format='%m/%d/%Y %I:%M %p', attribute='datetime')
    services = fields.Nested(ServiceSchema, many=True)
Автор: Alex Размещён: 11.08.2019 07:12
Вопросы из категории :
32x32