从具有孩子-父母关系的列表开始,创建一个以孩子为列表的父母

时间:2018-06-24 14:48:04

标签: python python-3.x

在数据库中,我有一个表,其中有一个外键(深度是多树中的深度级别):

id name depth   path   parent_id
 1   A    0     1         Null
 2   A1   1     1,2       1
 3   A2   1     1,3       1
 4   A11  2     1,2,4     2
 5   A111 3     1,2,4,5   4
 6   B    0     1         Null
 7   C    0     1         Null

我通过查询得到数据库中的所有类别。深度可以是无限的。

当我从数据库中获取查询时,我将根据每个记录和对象进行查询。

我需要将每个子对象附加到父对象,作为list属性中的元素:

 for item in items:

   create object A 
   has a A a parent; no - do nothing
   create object A1
   has object A1 a parent; yes A.list[0] = A1 
   create object A2
   has object A2 a parent; yes A.list[1] = A2 

创建列表后,我需要找回:

  for item in items:
    item.name
    has item.name A children - yes 
    -> for itemAkid in itemAchildren:
     itemAkid.name

1 个答案:

答案 0 :(得分:2)

我将假设您的查询为完整的子树生成行。

如果您对查询进行 sort 排序时,可以按深度颠倒的顺序创建关系,并将为先前深度创建的节点作为字典从父id到object保留下来。 / p>

class TreeNode:
    def __init__(id, name, children=()):
        self.id = id
        self.name = name
        self.children = children

query = '''\
    SELECT
        id, name, parent_id
    FROM <TABLENAME>
    WHERE <CRITERIA>
    ORDER_BY depth DESC
'''

cursor.execute(query, (parameters, ...))

roots = []
parentless = {}
for id, name, parent_id in cursor:
    children = tuple(parentless.pop(id, ()))
    object = TreeNode(id, name, children)
    if parent_id is not None:
        parentless.setdefault(parent_id, []).append(object)
    else:
        roots.append(object)

如果您使用的是产生映射模型类实例的ORM,则只需用游标上的循环替换为ORM结果上的循环(仍然使用相同的顺序),删除object = TreeNode(...)行,并从ORM为您生成的对象中检索idparent_id的值。在每个对象上将子级设置为新列表或元组属性。

因为位于最高depth值的叶节点会先被处理,所以在处理其父节点时,所有子节点都已被处理并添加到parentless[id]列表中。如果当前节点有任何子节点,则parentless.get(id, ())将产生它们的序列。

但是,如果您使用的是现有的ORM,请不要重新发明这个轮子。也不要使用邻接表,对于分层数据,有更好的SQL模型,例如closure tableModified Preorder Tree Traversal。对于Django,有a series of options available,特别是django-closuretreedjango-mpttdjango-treebeard。后者支持邻接表表结构,应该可以将现有模型直接移到it's support for adjacency lists