2011-02-04 19 views
11

Estoy construyendo un CMS básico en un matraz para un sitio orientado a iPhone y estoy teniendo un pequeño problema con algo. Tengo una base de datos muy pequeña con solo 1 tabla (páginas). Aquí está el modelo:Crear un árbol a partir de tablas autorreferenciales en 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) 

Como se puede ver, por sub páginas, que acaba de hacer referencia a otra objeto de página en el campo parent_id. Lo que intento hacer en el panel de administración es tener una lista desordenada anidada con todas las páginas anidadas en sus páginas principales. Tengo muy poca idea de cómo hacer esto. Todo lo que puedo pensar es la siguiente (que sólo funcionará (tal vez, yo no lo he probado) 2 niveles más abajo):

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) 

Entonces yo simplemente formatear en una lista en la plantilla. ¿Cómo puedo hacer que esto funcione con potencialmente más de 10 páginas anidadas?

¡Gracias montones por adelantado!


EDIT: He buscado un poco y encontró http://www.sqlalchemy.org/docs/orm/relationships.html#adjacency-list-relationships, por lo que añade

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

a la parte inferior de mi modelo Page. y estoy mirando recursivamente a través de todo y agregándolo a un árbol de objetos. Probablemente he hecho ningún sentido, pero esa es la mejor manera en que puedo describirlo


EDIT 2: que tenía un ir a hacer una función recursiva para recorrer automáticamente todas las páginas y generar un diccionario anidada grande con todas las páginas y sus hijos, pero se cae Python, así que creo que es sólo un bucle infinito ... aquí está la función

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 

y la página que estoy probando con:

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

¿Alguien tiene alguna idea?

Respuesta

14

Mira http://sqlamp.angri.ru/index.html

o http://www.sqlalchemy.org/trac/browser/examples/adjacency_list/adjacency_list.py

UPD: Por ejemplo adjacency_list.py declarativa

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 recursividad

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 

Uso consulta en EXA mple para recursive fetch data de db:

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

Gracias por eso, pero todavía está un poco por encima de mi cabeza. para el segundo enlace, hay una forma de hacerlo con la forma declarativa de definir modelos (¿eso es lo que usa la extensión del matraz para sqlalchemy)? –

+0

@Estin That joinedload_all call especifica "niños" N veces. En este caso 4, entonces el árbol solo recurrirá 4 veces. ¿Hay alguna manera de hacerlo recurrente una cantidad arbitraria de veces? ¿A menos que haya una forma programática de determinarlo fácilmente? –

+1

@Zoran, si planeó trabajos con árboles recurrentes esta no es la mejor opción, formas más poderosas es usar soluciones MPTT como http://sqlalchemy-mptt.readthedocs.org/en/latest/ > A menos que haya una programática forma de determinarlo fácilmente? - Puede almacenar en el nodo raíz su "nivel profundo". – estin

Cuestiones relacionadas