nested_sets.py :  » Database » SQLAlchemy » SQLAlchemy-0.6.0 » examples » nested_sets » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Database » SQLAlchemy 
SQLAlchemy » SQLAlchemy 0.6.0 » examples » nested_sets » nested_sets.py
"""Celko's "Nested Sets" Tree Structure.

http://www.intelligententerprise.com/001020/celko.jhtml

"""

from sqlalchemy import (create_engine,Column,Integer,String,select,case
    func)
from sqlalchemy.orm import sessionmaker,MapperExtension,aliased
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.
ealias = aliased(Employee)
print session.query(Employee).\
            filter(ealias.left.between(Employee.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.between(Employee.left, Employee.right)).\
    group_by(ealias.emp).\
    order_by(ealias.left):
    print "    " * indentation + str(employee)
    
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.