首页 > 解决方案 > 使用嵌套字段加载/反序列化对象

问题描述

我有两个sqlalchemy模型 -ServiceRecordService,有一个many-to-many关系,它是通过一个service_record_services表来解决的。我正在尝试ServiceRecord从以下 JSON 反序列化对象,并通过提供Service的从 db 加载对象。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)

架构:

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

标签: pythonsqlalchemymarshmallow

解决方案


事实证明,我所要做的就是将两个必填字段添加到我的 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)

推荐阅读