来自邻接列表的嵌套 JSON

hor*_*uzz 5 python json sqlalchemy adjacency-list flask

我有一个 Flask RESTful API 应用程序,它具有以下 SQLAlchemy 类,其中包含一个邻接列表的自引用密钥代表:

class Medications(db.Model):
    __tablename__ = 'medications'
    id = Column(Integer, primary_key=True)
    type = Column(String(64))
    name = Column(String(64))
    parent_id = Column(Integer, ForeignKey('medications.id'))
    children = relationship("Medications")
Run Code Online (Sandbox Code Playgroud)

我想要从 Medications 类返回的嵌套 JSON,如下所示

"objects": [
    {
      "children": [
        {
          "id": 4, 
          "name": "Child1", 
          "parent_id": 3, 
          "type": "Leaf"
        }, 
        {
          "id": 5, 
          "name": "Child2", 
          "parent_id": 3, 
          "type": "Leaf"
        }
      ], 
      "id": 3, 
      "name": "CardioTest", 
      "parent_id": null, 
      "type": "Parent"
    }
  ], 
Run Code Online (Sandbox Code Playgroud)

根据how-to-create-a-json-object-from-tree-data-structure-in-database我创建了序列化程序类

class JsonSerializer(object):
    """A mixin that can be used to mark a SQLAlchemy model class which
    implements a :func:`to_json` method. The :func:`to_json` method is used
    in conjuction with the custom :class:`JSONEncoder` class. By default this
    mixin will assume all properties of the SQLAlchemy model are to be visible
    in the JSON output. Extend this class to customize which properties are
    public, hidden or modified before being being passed to the JSON serializer.
    """

    __json_public__ = None
    __json_hidden__ = None
    __json_modifiers__ = None

    def get_field_names(self):
        for p in self.__mapper__.iterate_properties:
            yield p.key

    def to_json(self):

        field_names = self.get_field_names()

        public = self.__json_public__ or field_names
        hidden = self.__json_hidden__ or []
        modifiers = self.__json_modifiers__ or dict()

        rv = dict()
        for key in public:
            rv[key] = getattr(self, key)
        for key, modifier in modifiers.items():
            value = getattr(self, key)
            rv[key] = modifier(value, self)
        for key in hidden:
            rv.pop(key, None)
        return rv
Run Code Online (Sandbox Code Playgroud)

并将其子类化到我的药物类中,按照 class Medications(db.Model, JsonSerializer):

然后我调用Models.to_json()以获取我的序列化 JSON 输出,但很遗憾,该对象为空:{'parent_id': None, 'type': None, 'children': [], 'name': None, 'id': None}

但是,作为测试,如果我创建一个 Flask Restless 端点,按照

manager = flask.ext.restless.APIManager(app, flask_sqlalchemy_db=db)
manager.create_api(Medications, methods=['GET'])
Run Code Online (Sandbox Code Playgroud)

我得到以下输出:

"objects": [
    {
      "children": [
        {
          "id": 4, 
          "name": "Child1", 
          "parent_id": 3, 
          "type": "Leaf"
        }, 
        {
          "id": 5, 
          "name": "Child2", 
          "parent_id": 3, 
          "type": "Leaf"
        }
      ], 
      "id": 3, 
      "name": "CardioTest", 
      "parent_id": null, 
      "type": "Parent"
    }, 
    {
      "children": [], 
      "id": 4, 
      "name": "Child1", 
      "parent_id": 3, 
      "type": "Leaf"
    }, 
    {
      "children": [], 
      "id": 5, 
      "name": "Child2", 
      "parent_id": 3, 
      "type": "Leaf"
    }
  ], 
Run Code Online (Sandbox Code Playgroud)

以及一些分页信息。

很好奇为什么我从使用 JsonSerializer 类的方法中得到一个空字典。我会使用 Flask Restless 方法,但由于我使用 Flask 作为 wsgi 应用程序,它会搞砸我的端点,另外,children: []输出中不需要的节点。

hor*_*uzz 3

我的问题的解决方案最终是使用具有嵌套模式的棉花糖(在这篇文章create-a-tree-from-self-referential-tables-in-sqlalchemy 的帮助下,ala

# create SQLAlchemy object
record = db.session.query(Medications). \
    options(joinedload_all("children", "children",
                           "children", "children",
                           "children", "children")).first()


class TestSchema(Schema):
    name = fields.Str()
    type = fields.Str()
    id = fields.Int(dump_only=True)
    parent_id = fields.Int(dump_only=True)
    children = fields.Nested('self', many=True)

schema = TestSchema()

result = schema.dump(record)
Run Code Online (Sandbox Code Playgroud)

工作起来就像一个魅力,不需要实现递归方法来构建树。