我正在尝试向应用程序添加类似 reddit 的评论,我决定使用闭包表模式来组织数据库。我的应用程序数据库看起来有点像这样:
帖子
+----+-------+
| id | title |
+----+-------+
| 1 | Hello |
+----+-------+
评论
+----+-----------+---------+------+
| id | parent_id | post_id | text |
+----+-----------+---------+------+
| 1 | NULL | 1 | ... |
| 2 | 1 | 1 | ... |
| 3 | 2 | 1 | ... |
| 4 | 3 | 1 | ... |
| 5 | 3 | 1 | ... |
| 6 | 5 | 1 | ... |
| 7 | NULL | 1 | ... |
| 8 | 7 | 1 | ... |
| 9 | 4 | 1 | ... |
+----+-----------+---------+------+
评论路径
+-----------+----------+-------+
| parent_id | child_id | depth |
+-----------+----------+-------+
| 1 | 1 | 0 |
| 2 | 2 | 0 |
| 1 | 2 | 1 |
| 3 | 3 | 0 |
| 2 | 3 | 1 |
| 1 | 3 | 2 |
| 4 | 4 | 0 |
| 3 | 4 | 1 |
| 2 | 4 | 2 |
| 1 | 4 | 3 |
[...snip...]
现在,我正在运行这个查询:
SELECT c.*, p.*
FROM comments AS c
JOIN comment_paths AS p
ON c.id = p.child_id
WHERE p.parent_id IN
(SELECT c2.id FROM comments AS c2 WHERE c2.parent_id IS NULL AND c2.post_id = 1)
获取基于他们的评论列表post_id
。返回的数据是:
+------+-------------+-----------+--------+-------------+------------+---------+
| c.id | c.parent_id | c.post_id | c.text | p.parent_id | p.child_id | p.depth |
+------+-------------+-----------+--------+-------------+------------+---------+
| 1 | NULL | 1 | ... | 1 | 1 | 0 |
| 2 | 1 | 1 | ... | 1 | 2 | 1 |
| 3 | 2 | 1 | ... | 1 | 3 | 2 |
| 4 | 3 | 1 | ... | 1 | 4 | 3 |
| 5 | 3 | 1 | ... | 1 | 5 | 3 |
| 6 | 5 | 1 | ... | 1 | 6 | 4 |
| 9 | 4 | 1 | ... | 1 | 9 | 4 |
| 7 | NULL | 1 | ... | 7 | 7 | 0 |
| 8 | 7 | 1 | ... | 7 | 8 | 1 |
+------+-------------+-----------+--------+-------------+------------+---------+
这代表树:
[1]
|[2]
| |[3]
| |[4]
| | |[9]
| [5]
| |[6]
[7]
|[8]
但是,我正在努力将返回的数据转换为 Python 树结构。本质上,我的目标是这个问题和这个关于最终输出 (HTML) 的问题,但我真的不想求助于递归 SQL 语句,因为我已经有了这些信息。我认为某种递归是必要的,因为我希望最终得到与此类似的结构:
[
{
'id': 1,
...
'children': [
{
'id': 2,
...
'children': [ ... ]
}
]
},
{
'id': 7,
...
'children': [
{
'id': 8,
...
}
]
}
]
基本上是一个嵌套的字典列表,所以我可以使用Jinja 的递归循环遍历它们。有人有想法吗?
谢谢!
编辑 2013-04-17
乱七八糟,我有一个“工作”的解决方案,虽然它做了很多迭代,所以我不想把它标记为这个问题的答案。我使用的解决方案是:
comment_set = ... # previous query to grab data set
def create_tree(parent):
parent['children'] = []
for record in comment_set:
if record['parent_id'] == parent['id']:
parent['children'].append(create_tree(record))
return parent
comment_tree = []
for record in comment_set:
if record['parent_id'] is None: # if this is the start of a tree
comment_tree.append(create_tree(record))
comment_set
这并不理想,因为它会在每次调用时迭代create_tree()
,即集合中的每条记录。然而,这是我目前拥有的最好的。有人有什么想法吗?