--- /dev/null
+"""
+An example of a dictionary-of-dictionaries structure mapped using
+an adjacency list model
+
+"""
+
+from sqlalchemy import MetaData, Table, Column, Sequence, ForeignKey,\
+ Integer, String, create_engine
+
+from sqlalchemy.orm import sessionmaker, mapper, relation, backref,\
+ eagerload_all
+
+from sqlalchemy.orm.collections import attribute_mapped_collection
+
+metadata = MetaData()
+
+tree_table = Table('tree', metadata,
+ Column('id', Integer, primary_key=True),
+ Column('parent_id', Integer, ForeignKey('tree.id')),
+ Column('name', String(50), nullable=False)
+)
+
+class TreeNode(object):
+ 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 dump_tree(node, indent=0):
+
+ return " " * indent + repr(node) + \
+ "\n" + \
+ "".join([
+ dump_tree(c, indent +1)
+ for c in node.children.values()]
+ )
+
+
+mapper(TreeNode, tree_table, properties={
+ 'children': relation(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=tree_table.c.id),
+
+ # children will be represented as a dictionary
+ # on the "name" attribute.
+ collection_class=attribute_mapped_collection('name'),
+ )
+ })
+
+if __name__ == '__main__':
+ engine = create_engine('sqlite://', echo=True)
+
+ def msg(msg):
+ print "\n\n\n" + "-" * len(msg)
+ print msg
+ print "-" * len(msg)
+
+ msg("Creating Tree Table:")
+
+ metadata.create_all(engine)
+
+ # session. using expire_on_commit=False
+ # so that the session's contents are not expired
+ # after each transaction commit.
+ session = sessionmaker(engine, expire_on_commit=False)()
+
+ node = TreeNode('rootnode')
+ node.append('node1')
+ node.append('node3')
+
+ node2 = TreeNode('node2')
+ node2.append('subnode1')
+ node.children['node2'] = node2
+ node.children['node2'].append('subnode2')
+
+ msg("Created new tree structure:")
+
+ print dump_tree(node)
+
+ msg("flush + commit:")
+
+ session.add(node)
+ session.commit()
+
+ msg("Tree After Save:")
+
+ print dump_tree(node)
+
+ node.append('node4')
+ node.children['node4'].append('subnode3')
+ node.children['node4'].append('subnode4')
+ node.children['node4'].children['subnode3'].append('subsubnode1')
+
+ # mark node1 as deleted and remove
+ session.delete(node.children['node1'])
+
+ msg("Removed node1. flush + commit:")
+ session.commit()
+
+ print "\n\n\n----------------------------"
+ print "Tree After Save:"
+ print "----------------------------"
+
+ # expire the "children" collection so that
+ # it reflects the deletion of "node1".
+ session.expire(node, ['children'])
+ print dump_tree(node)
+
+ msg("Emptying out the session entirely, "
+ "selecting tree on root, using eager loading to join four levels deep.")
+ session.expunge_all()
+ node = session.query(TreeNode).\
+ options(eagerload_all("children", "children",
+ "children", "children")).\
+ filter(TreeNode.name=="rootnode").\
+ first()
+
+ msg("Full Tree:")
+ print dump_tree(node)
+
+ msg( "Marking root node as deleted, flush + commit:" )
+
+ session.delete(node)
+ session.commit()
+++ /dev/null
-"""A basic Adjacency List model tree."""
-
-from sqlalchemy import MetaData, Table, Column, Sequence, ForeignKey
-from sqlalchemy import Integer, String
-from sqlalchemy.orm import create_session, mapper, relation, backref
-from sqlalchemy.orm.collections import attribute_mapped_collection
-
-metadata = MetaData('sqlite:///')
-metadata.bind.echo = True
-
-trees = Table('treenodes', metadata,
- Column('id', Integer, Sequence('treenode_id_seq', optional=True),
- primary_key=True),
- Column('parent_id', Integer, ForeignKey('treenodes.id'), nullable=True),
- Column('name', String(50), nullable=False))
-
-
-class TreeNode(object):
- """a rich Tree class which includes path-based operations"""
- def __init__(self, name):
- self.name = name
- self.parent = None
- self.id = None
- self.parent_id = None
- def append(self, node):
- if isinstance(node, str):
- node = TreeNode(node)
- node.parent = self
- self.children[node.name] = node
- def __repr__(self):
- return self._getstring(0, False)
- def __str__(self):
- return self._getstring(0, False)
- def _getstring(self, level, expand = False):
- s = (' ' * level) + "%s (%s,%s, %d)" % (
- self.name, self.id,self.parent_id,id(self)) + '\n'
- if expand:
- s += ''.join([n._getstring(level+1, True)
- for n in self.children.values()])
- return s
- def print_nodes(self):
- return self._getstring(0, True)
-
-mapper(TreeNode, trees, properties={
- 'children': relation(TreeNode, cascade="all",
- backref=backref("parent", remote_side=[trees.c.id]),
- collection_class=attribute_mapped_collection('name'),
- lazy=False, join_depth=3)})
-
-print "\n\n\n----------------------------"
-print "Creating Tree Table:"
-print "----------------------------"
-
-trees.create()
-
-node2 = TreeNode('node2')
-node2.append('subnode1')
-node = TreeNode('rootnode')
-node.append('node1')
-node.append(node2)
-node.append('node3')
-node.children['node2'].append('subnode2')
-
-print "\n\n\n----------------------------"
-print "Created new tree structure:"
-print "----------------------------"
-
-print node.print_nodes()
-
-print "\n\n\n----------------------------"
-print "Flushing:"
-print "----------------------------"
-
-session = create_session()
-session.add(node)
-session.flush()
-
-print "\n\n\n----------------------------"
-print "Tree After Save:"
-print "----------------------------"
-
-print node.print_nodes()
-
-node.append('node4')
-node.children['node4'].append('subnode3')
-node.children['node4'].append('subnode4')
-node.children['node4'].children['subnode3'].append('subsubnode1')
-del node.children['node1']
-
-print "\n\n\n----------------------------"
-print "Modified the tree"
-print "(added node4, node4/subnode3, node4/subnode4,"
-print "node4/subnode3/subsubnode1, deleted node1):"
-print "----------------------------"
-
-print node.print_nodes()
-
-print "\n\n\n----------------------------"
-print "Flushing:"
-print "----------------------------"
-session.flush()
-
-print "\n\n\n----------------------------"
-print "Tree After Save:"
-print "----------------------------"
-
-print node.print_nodes()
-
-nodeid = node.id
-
-print "\n\n\n----------------------------"
-print "Clearing session, selecting "
-print "tree new where node_id=%d:" % nodeid
-print "----------------------------"
-
-session.expunge_all()
-t = session.query(TreeNode).filter(TreeNode.id==nodeid)[0]
-
-print "\n\n\n----------------------------"
-print "Full Tree:"
-print "----------------------------"
-print t.print_nodes()
-
-print "\n\n\n----------------------------"
-print "Marking root node as deleted"
-print "and flushing:"
-print "----------------------------"
-session.delete(t)
-session.flush()