在SQLalchemy中从自引用表创建树
我正在为面向iPhone的站点在烧瓶中构建基本的CMS,但在某些方面遇到了一些麻烦。我有一个非常小的数据库,只有1个表(页面)。这是模型:
class Page(db.Model):
__tablename__ = 'pages'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
content = db.Column(db.Text, nullable=False)
parent_id = db.Column(db.Integer, db.ForeignKey("pages.id"), nullable=True)
如您所见,对于子页面,它们仅引用parent_id
字段中的另一个页面对象。我要在管理面板中执行的操作是将所有页面嵌套在其父页面中的嵌套无序列表。我对如何执行此操作几乎一无所知。我所能想到的是以下内容(只能正常工作(也许-
我尚未测试过)2级):
pages = Page.query.filter_by(parent_id=None)
for page in pages:
if Page.query.filter_by(parent_id=page.id):
page.sub_pages = Page.query.filter_by(parent_id=page.id)
然后,我将其格式化为模板中的列表。如何处理可能超过10个嵌套的页面?
预先感谢堆!
编辑:
我四处看看,发现http://www.sqlalchemy.org/docs/orm/relationships.html#adjacency-list-
relationships,所以我添加了
children = db.relationship("Page", backref=db.backref("parent", remote_side=id))
到我Page
模型的底部 我正在递归地查看所有内容并将其添加到对象树中。我可能没有任何意义,但这是我形容的最佳方式
编辑2:
我可以制作一个递归函数来遍历所有页面,并生成一个包含所有页面及其子元素的大嵌套字典,但是它不断使python崩溃,所以我认为这只是一个无限循环…这就是功能
def get_tree(base_page, dest_dict):
dest_dict = { 'title': base_page.title, 'content': base_page.content }
children = base_page.children
if children:
dest_dict['children'] = {}
for child in children:
get_tree(base_page, dest_dict)
else:
return
和我正在测试的页面:
@app.route('/test/')
def test():
pages = Page.query.filter_by(parent_id=None)
pages_dict = {}
for page in pages:
get_tree(page, pages_dict)
return str(pages_dict)
任何人有任何想法吗?
-
看看http://sqlamp.angri.ru/index.html
或http://www.sqlalchemy.org/trac/browser/examples/adjacency_list/adjacency_list.py
UPD: 对于adjacency_list.py声明性示例
from sqlalchemy.ext.declarative import declarative_base Base = declarative_base(metadata=metadata) class TreeNode(Base): __tablename__ = 'tree' id = Column(Integer, primary_key=True) parent_id = Column(Integer, ForeignKey('tree.id')) name = Column(String(50), nullable=False) children = relationship('TreeNode', # cascade deletions cascade="all", # many to one + adjacency list - remote_side # is required to reference the 'remote' # column in the join condition. backref=backref("parent", remote_side='TreeNode.id'), # children will be represented as a dictionary # on the "name" attribute. collection_class=attribute_mapped_collection('name'), ) def __init__(self, name, parent=None): self.name = name self.parent = parent def append(self, nodename): self.children[nodename] = TreeNode(nodename, parent=self) def __repr__(self): return "TreeNode(name=%r, id=%r, parent_id=%r)" % ( self.name, self.id, self.parent_id )
修正递归
def get_tree(base_page, dest_dict): dest_dict = { 'title': base_page.title, 'content': base_page.content } children = base_page.children if children: dest_dict['children'] = {} for child in children: get_tree(child, dest_dict) else: return
在示例中使用查询从数据库递归获取数据:
# 4 level deep node = session.query(TreeNode).\ options(joinedload_all("children", "children", "children", "children")).\ filter(TreeNode.name=="rootnode").\ first()