2011-02-04 102 views
11

我正在爲一個面向iPhone的站點構建一個基本的CMS容器,並且我遇到了一些問題。我有一個非常小的數據庫只有一個表(頁)。這裏的模型:從SQLalchemy中的自引用表創建一棵樹

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:我在做一個遞歸函數在所有的網頁上投放和產生一個大的嵌套的字典了個去與所有的網頁和他們的孩子,但它總是崩潰蟒蛇,所以我認爲這只是一個無限循環......這裏的功能

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) 

任何人有任何想法?

回答

14

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 

使用在EXA查詢從db遞歸獲取數據:

# 4 level deep 
node = session.query(TreeNode).\ 
         options(joinedload_all("children", "children", 
               "children", "children")).\ 
         filter(TreeNode.name=="rootnode").\ 
         first() 
+0

謝謝你,但它仍然是我的頭。對於第二個鏈接,是否有一個用定義模型的聲明性基本方式來做到這一點(這是sqlalchemy使用的擴展插件)? – 2011-02-05 00:24:44