Flask-restful: 将复杂对象编组为JSON

17

我有一个关于flask restful扩展的问题。我刚开始使用它并遇到了一个问题。我有与多个一对一关系连接的flask-sqlalchemy实体,并且我希望restful端点使用marshaller以json格式返回父实体及其所有子实体。在我的情况下,Set包含许多参数。我查看了flask-restful文档,但没有任何解释如何解决这种情况。

似乎我缺少一些明显的东西,但无法找到任何解决方案。以下是我的代码:

# entities
class Set(db.Model):
    id = db.Column("id", db.Integer, db.Sequence("set_id_seq"), primary_key=True)
    title = db.Column("title", db.String(256))

    parameters = db.relationship("Parameters", backref="set", cascade="all")


class Parameters(db.Model):
    id = db.Column("id", db.Integer, db.Sequence("parameter_id_seq"), primary_key=True)
    flag = db.Column("flag", db.String(256))
    value = db.Column("value", db.String(256))
    set_id = db.Column("set_id", db.Integer, db.ForeignKey("set.id"))


# marshallers

from flask.ext.restful import fields

parameter_marshaller = {
    "flag": fields.String,
    "value": fields.String
}

set_marshaller = {
    'id': fields.String,
    'title': fields.String,
    'parameters': fields.List(fields.Nested(parameter_marshaller))
}

# endpoint    

class SetApi(Resource):

    @marshal_with(marshallers.set_marshaller)
    def get(self, set_id):
        entity = Set.query.get(set_id)
        return entity


restful_api = Api(app)
restful_api.add_resource(SetApi, "/api/set/<int:set_id>")
现在当我调用/api/set/1时,我会得到服务器错误:TypeError:'Set'对象不可订阅。所以我需要一种正确定义set_marshaller的方法,以便该端点返回此JSON。
{
  "id": : "1",
  "title": "any-title",
  "parameters": [
       {"flag": "any-flag", "value": "any-value" },
       {"flag": "any-flag", "value": "any-value" },
       .....
   ]
}

我非常感谢任何帮助。

2个回答

26

我自己找到了解决那个问题的方法。

在尝试使用flask-restful时,我发现自己犯了几个错误:

首先,set_marshaller应该像这样:

set_marshaller = {
    'id': fields.String,
    'title': fields.String,
    'parameters': fields.Nested(parameter_marshaller)
}

如果参数是列表类型,并且需要被编组成 json 列表,那么不安分的 marshaller 可以处理这种情况。

另一个问题在于 API 中的 Set 参数采用了惰性加载,所以当我尝试对 Set 进行编组时,会出现 KeyError: 'parameters' 错误,因此我需要显式地加载参数,如下所示:

class SetApi(Resource):

     @marshal_with(marshallers.set_marshaller)
     def get(self, set_id):
        entity = Set.query.get(set_id)
        entity.parameters # loads parameters from db
        return entity

或者另一个选择是改变模型关系:

parameters = db.relationship("Parameters", backref="set", cascade="all" lazy="joined")

1
你救了我一大堆麻烦。 - Atif Shafi

9

这是对Zygimantas回答的补充:

我正在使用Flask-RESTful,这是解决嵌套属性加载的方法。

您可以将可调用对象传递给marshal装饰器:

class OrgsController(Resource):
    @marshal_with(Organization.__json__())
    def get(self):
        return g.user.member.orgs

然后更新模型以返回其自身实体的资源字段。嵌套实体将因此相对于其实体返回其实体的资源字段。

class Organization(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    ...

    @staticmethod
    def __json__(group=None):
        _json = {
            'id': fields.String,
            'login': fields.String,
            'description': fields.String,
            'avatar_url': fields.String,
            'paid': fields.Boolean,
        }

        if group == 'flat':
            return _json

        from app.models import Repository
        _json['repos'] = fields.Nested(Repository.__json__('flat'))

        return _json

class Repository(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    owner_id = db.Column(db.Integer, db.ForeignKey('organization.id'))
    owner = db.relationship('Organization', lazy='select', backref=db.backref('repos', lazy='select'), foreign_keys=[owner_id])
    ...

    @staticmethod
    def __json__(group=None):
        _json = {
            'id': fields.String,
            'name': fields.String,
            'updated_at': fields.DateTime(dt_format='iso8601'),
        }

        if group == 'flat':
            return _json

        from app.models import Organization
        _json['owner'] = fields.Nested(Organization.__json__('flat'))

        return _json

这提供了我所需要的表示,并支持延迟加载:
[
    {
        "avatar_url": "https://avatars.githubusercontent.com/u/18945?v=3",
        "description": "lorem ipsum.",
        "id": "1805",
        "login": "foobar",
        "paid": false,
        "repos":
            [
                {
                    "id": "9813",
                    "name": "barbaz",
                    "updated_at": "2014-01-23T13:51:30"
                },
                {
                    "id": "12860",
                    "name": "bazbar",
                    "updated_at": "2015-04-17T11:06:36"
                }
            ]
    }
]

我喜欢以下几点:

1)这种方法允许我为每个实体定义我的资源字段,并且在整个应用程序的所有资源路由中都可用。

2)group参数允许我按照自己的意愿自定义表示方式。我只在此处有“flat”,但可以编写任何逻辑并传递到更深层嵌套的对象中。

3)仅在必要时加载实体。


这正是我一直在寻找的解决方案,谢谢!我喜欢它将编组对象与数据库模型耦合在一起的方式。更容易管理。 - CaffeinatedMike

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接