2011-02-04 27 views
11

Tôi đang xây dựng CMS cơ bản trong bình cho một trang web hướng đến iPhone và tôi đang gặp một chút rắc rối với một thứ gì đó. Tôi có một cơ sở dữ liệu rất nhỏ chỉ với 1 bảng (trang). Đây là mô hình:Tạo một cây từ các bảng tự tham chiếu trong 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) 

Như bạn có thể thấy, đối với trang con, họ chỉ tham chiếu một đối tượng trang khác trong trường parent_id. Những gì tôi đang cố gắng làm trong bảng quản trị là có một danh sách lồng nhau không có thứ tự với tất cả các trang được lồng trong trang cha của chúng. Tôi có rất ít ý tưởng về cách làm điều này. Tất cả tôi có thể nghĩ đến là sau (mà sẽ chỉ làm việc (có thể-I đã không kiểm tra nó) 2 cấp độ xuống):

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) 

tôi sẽ sau đó chỉ cần định dạng nó thành một danh sách trong mẫu. Làm cách nào để tôi thực hiện công việc này với hơn 10 trang lồng nhau?

Cảm ơn đống trước!


EDIT: Tôi đã nhìn xung quanh một chút và thấy http://www.sqlalchemy.org/docs/orm/relationships.html#adjacency-list-relationships, vì vậy tôi thêm

children = db.relationship("Page", backref=db.backref("parent", remote_side=id)) 

để dưới cùng của mô hình Page tôi. và tôi đang xem xét đệ quy xuyên qua mọi thứ và thêm nó vào một cây đối tượng. Tôi đã có thể làm cho không có ý nghĩa, nhưng đó là cách tốt nhất mà tôi có thể mô tả nó


EDIT 2: tôi đã một đi vào thực hiện một hàm đệ quy để chạy qua tất cả các trang và tạo ra một từ điển lồng lớn với tất cả các trang và con cái của họ, nhưng nó vẫn không ngừng đâm python vì vậy tôi nghĩ rằng nó chỉ là một vòng lặp vô hạn ... đây là chức năng

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 

và trang tôi đang thử nghiệm nó với:

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

có ai có ý tưởng nào không?

Trả lời

14

Nhìn vào http://sqlamp.angri.ru/index.html

hoặc http://www.sqlalchemy.org/trac/browser/examples/adjacency_list/adjacency_list.py

UPD: Ví dụ adjacency_list.py declarative

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 
       )  

Fix đệ quy

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 

Sử dụng truy vấn trong exa mple cho dữ liệu tìm nạp đệ quy từ db:

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

Cảm ơn điều đó, nhưng nó vẫn còn hơi hơn đầu tôi. cho liên kết thứ hai là có một wak để làm điều đó với cách xác định cơ sở khai báo mô hình (đó là những gì mở rộng bình cho sqlalchemy sử dụng)? –

+0

@Estin Cuộc gọi joinload_all đó chỉ định "con" N lần. Trong trường hợp này là 4, và do đó cây sẽ chỉ tái sử dụng 4 lần. Có cách nào để làm cho nó chấp nhận một lượng thời gian tùy ý không? Trừ khi có một cách lập trình để xác định nó một cách dễ dàng? –

+1

@ Zoran, nếu bạn lên kế hoạch làm việc với việc thu thập cây này không phải là lựa chọn tốt nhất, cách mạnh mẽ hơn là sử dụng các giải pháp MPTT như http://sqlalchemy-mptt.readthedocs.org/en/latest/ > Trừ khi có một chương trình cách để xác định nó dễ dàng? - Bạn có thể lưu trữ trên nút gốc của mình "mức độ sâu" của mình. – estin

Các vấn đề liên quan