来自邻接表的嵌套 JSON

Nested JSON from adjacency list

我有一个 Flask RESTful API 应用程序,它具有以下 SQLAlchemy class 和一个代表邻接列表的自引用键:

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

我想要从 Medications class 返回的嵌套 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"
    }
  ], 

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

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

并根据 class Medications(db.Model, JsonSerializer):

将其子class 编辑到我的药物 class

然后我调用 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'])

我得到以下输出:

"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"
    }
  ], 

以及一些分页信息。

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

我的问题的解决方案最终是使用带有嵌套模式的 Marshmallow(在这个 post creating-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)

工作得很好,不需要实现递归方法来构建树。