diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2008-09-18 22:14:29 +0000 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2008-09-18 22:14:29 +0000 |
| commit | 16dd8aab74d6936d8ce81c377a266cc6c94f18c9 (patch) | |
| tree | ed83366c97d781810941967d4278228473b7f1d7 /examples | |
| parent | 9632a752d2661326aabbfc02185f17318d41aef2 (diff) | |
| download | sqlalchemy-16dd8aab74d6936d8ce81c377a266cc6c94f18c9.tar.gz | |
"nested sets" example. needs work.
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/nested_sets/nested_sets.py | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/examples/nested_sets/nested_sets.py b/examples/nested_sets/nested_sets.py new file mode 100644 index 000000000..8af9aed45 --- /dev/null +++ b/examples/nested_sets/nested_sets.py @@ -0,0 +1,104 @@ +"""Celko's "Nested Sets" Tree Structure. + +http://www.intelligententerprise.com/001020/celko.jhtml + +""" + +from sqlalchemy import * +from sqlalchemy.orm import * +from sqlalchemy.orm import attributes +from sqlalchemy.ext.declarative import declarative_base + +engine = create_engine('sqlite://', echo=True) +Base = declarative_base() + +class NestedSetExtension(MapperExtension): + def before_insert(self, mapper, connection, instance): + if not instance.parent: + instance.left = 1 + instance.right = 2 + else: + personnel = mapper.mapped_table + right_most_sibling = connection.scalar( + select([personnel.c.rgt]).where(personnel.c.emp==instance.parent.emp) + ) + + connection.execute( + personnel.update(personnel.c.rgt>=right_most_sibling).values( + lft = case( + [(personnel.c.lft>right_most_sibling, personnel.c.lft + 2)], + else_ = personnel.c.lft + ), + rgt = case( + [(personnel.c.rgt>=right_most_sibling, personnel.c.rgt + 2)], + else_ = personnel.c.rgt + ) + ) + ) + instance.left = right_most_sibling + instance.right = right_most_sibling + 1 + + # before_update() would be needed to support moving of nodes + # after_delete() would be needed to support removal of nodes. + # [ticket:1172] needs to be implemented for deletion to work as well. + +class Employee(Base): + __tablename__ = 'personnel' + __mapper_args__ = { + 'extension':NestedSetExtension(), + 'batch':False # allows extension to fire for each instance before going to the next. + } + + parent = None + + emp = Column(String, primary_key=True) + + left = Column("lft", Integer, nullable=False) + right = Column("rgt", Integer, nullable=False) + + def __repr__(self): + return "Employee(%s, %d, %d)" % (self.emp, self.left, self.right) + +Base.metadata.create_all(engine) + +session = sessionmaker(bind=engine)() + +albert = Employee(emp='Albert') +bert = Employee(emp='Bert') +chuck = Employee(emp='Chuck') +donna = Employee(emp='Donna') +eddie = Employee(emp='Eddie') +fred = Employee(emp='Fred') + +bert.parent = albert +chuck.parent = albert +donna.parent = chuck +eddie.parent = chuck +fred.parent = chuck + +# the order of "add" is important here. elements must be added in +# the order in which they should be INSERTed. +session.add_all([albert, bert, chuck, donna, eddie, fred]) +session.commit() + +print session.query(Employee).all() + +# 1. Find an employee and all his/her supervisors, no matter how deep the tree. +# (the between() operator in SQLAlchemy has a bug here, [ticket:1171]) +ealias = aliased(Employee) +print session.query(Employee).\ + filter(ealias.left>=Employee.left).filter(ealias.left<=Employee.right).\ + filter(ealias.emp=='Eddie').all() + +#2. Find the employee and all his/her subordinates. (This query has a nice symmetry with the first query.) +print session.query(Employee).\ + filter(Employee.left.between(ealias.left, ealias.right)).\ + filter(ealias.emp=='Chuck').all() + +#3. Find the level of each node, so you can print the tree as an indented listing. +for indentation, employee in session.query(func.count(Employee.emp).label('indentation') - 1, ealias).\ + filter(ealias.left>=Employee.left).filter(ealias.left<=Employee.right).\ + group_by(ealias.emp).\ + order_by(ealias.left): + print " " * indentation + str(employee) + |
