Treestructure in SQL

Peter Otten __peter__ at web.de
Tue Nov 25 19:53:25 EST 2003


Thomas Weholt wrote:

> I need to define tree-like structure for content categories in a simple
> CMS project. I got a table in a SQLite database which looks like this :

SQL does not support tree structures, so the use of a relational db is
probably not the best choice. 

> 
>     CREATE TABLE category (
>       CAT_ID INTEGER PRIMARY KEY,
>       CAT_PARENT_ID integer,
>       CAT_NAME varchar(100) NOT NULL,
>       CAT_DESCRIPTION varchar(250),
>     );
> 
> It makes is possible to create a tree of categories, linking subnodes in
> the the tree to parent nodes using the CAT_PARENT_ID and it works nice,
> but I'm having trouble visualizing it.

Would that mean you already have a userfriendly way to enter the data?

> Say I got these records in the table :
> Id, Parent-id, Name
> 1, 0, Games
> 2, 1, Counter-strike
> 3, 1, Boardgames
> 4, 0, Programming
> 5, 4, Python
> 6, 4, XML
> 7, 5, Web
> 
> Where 0 as parent-id symbolizes "root"-level of the treestructure.
> 
> Now I need either a SQL-statement compatible with SQLite or some code
> snippet that will give me :
> 
> Games
> Games, Counter-strike
> Games, Boardgames
> Programming
> Programming, Python
> Programming, Python, Web
> Programming, XML
> 
> Any clues or hints??

Well, I wanted to try out sqlite anyway, so I made a little Python wrapper
around your table to print it like shown above.
However, I ended up with much of the data in memory, so I still cannot see
why you favoured a db over pickling a tree of Python objects.

<code>
import sqlite, sys

class Node(object):
    def __init__(self, tree, parentId, id, name):
        self.tree = tree
        self.id = id
        self.parentId = parentId
        self.name = name

    def children(self):
        return self.tree.childrenFor(self.id)
    def __str__(self):
        return self.name
    def printSelf(self, parents):
        if parents is None:
            parents = []
        parents.append(self)
        print ", ".join([str(n) for n in parents])
        for child in self.children():
            child.printSelf(parents)
        parents.pop()

class RootNode(Node):
    def printSelf(self):
        for child in self.children():
            child.printSelf([])

class Tree(object):
    def __init__(self):
        self.conn = sqlite.connect(db="db", mode=755)
        self.cursor = self.conn.cursor()
    def close(self):
        self.conn.close()
    def childrenFor(self, id):
        self.cursor.execute("""
        SELECT
            CAT_PARENT_ID,
            CAT_ID,
            CAT_NAME
        FROM category
        WHERE CAT_PARENT_ID = %d;""" % id)
        return [Node(self, *row) for row in self.cursor]

def createDb():
    conn = sqlite.connect(db="db", mode=755)
    cursor = conn.cursor()
    sql_create = """
      CREATE TABLE category (
      CAT_ID INTEGER PRIMARY KEY,
      CAT_PARENT_ID integer,
      CAT_NAME varchar(100) NOT NULL,
      CAT_DESCRIPTION varchar(250)
    );"""
    cursor.execute(sql_create)

    #Id, Parent-id, Name
    records = [
    (1, 0, "Games"),
    (2, 1, "Counter-strike"),
    (3, 1, "Boardgames"),
    (4, 0, "Programming"),
    (5, 4, "Python"),
    (6, 4, "XML"),
    (7, 5, "Web")
    ]
    for record in records:
        sql_insert = "INSERT INTO category VALUES (%d, %d, '%s', '');" %
record
        cursor.execute(sql_insert)
    conn.commit()
    conn.close()

def printDb():
    tree = Tree()
    root = RootNode(tree, 0, 0, "<root>")
    root.printSelf()

def help():
    print """
    provide one of the following commands:
        create - creates a tiny sample database "db"
        print - prints the tree
    """

if __name__ == "__main__":
    import warnings
    warnings.filterwarnings("ignore",  module="sqlite")
    try:
        cmd = sys.argv[1]
    except IndexError:
        help()
    else:
        {"create": createDb, "print": printDb}.get(cmd, help)()
</code>

The script includes the table generation code, in case anyone other than the
OP wants to play with it.

Peter

PS: In the spirit of "It's easier to ask forgiveness than permission", is
there a generally agreed upon upper size limit for usenet posts?







More information about the Python-list mailing list