【问题标题】:Flask-restful: marshal complex object to jsonFlask-restful:将复杂对象编组为 json
【发布时间】:2014-03-28 23:04:35
【问题描述】:

我有一个关于烧瓶宁静扩展的问题。我刚开始使用它并面临一个问题。我有flask-sqlalchemy 连接多对一关系的实体,我希望那个宁静的端点使用编组器返回父实体及其在json 中的所有子实体。就我而言, Set 包含许多参数。我查看了flask-restful docs,但没有任何解释如何解决这个问题。

似乎我遗漏了一些明显的东西,但无法找到任何解决方案。 这是我的代码:

# 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' object is unsubscriptable

所以我需要一种方法来正确定义端点返回此 json 的 set_marshaller:

{
  "id": : "1",
  "title": "any-title",
  "parameters": [
       {"flag": "any-flag", "value": "any-value" },
       {"flag": "any-flag", "value": "any-value" },
       .....
   ]
}

感谢您的帮助。

【问题讨论】:

    标签: python json rest flask flask-restful


    【解决方案1】:

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

    在玩了flask-restful之后,我发现我犯了一些错误:

    首先set_marshaller 应该是这样的:

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

    如果参数是列表,Restless marshaller 可以处理大小写并编组到 json 列表。

    另一个问题是 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")
    

    【讨论】:

    • 你救了我的痛苦
    【解决方案2】:

    这是对Zygimantasanswer 的补充:

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

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

    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 参数如何允许我根据需要自定义表示。我这里只有“平面”,但可以编写任何逻辑并将其传递给更深的嵌套对象。

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

    【讨论】:

    • 这正是我一直在寻找的解决方案,谢谢!我喜欢它如何使编组对象与数据库模型耦合。更容易管理。
    猜你喜欢
    • 2015-03-21
    • 1970-01-01
    • 2018-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-21
    • 1970-01-01
    相关资源
    最近更新 更多