class HierarchicalModel(models.Model):
"A generic hierarchical database model."
left_visit = models.IntegerField(primary_key=True)
right_visit = models.IntegerField(db_index=True)
class Meta:
abstract = True
@classmethod
def getRoot(cls):
"Get the unique root node for this table."
return cls.objects.get(left_visit=1)
@classmethod
def initRoot(cls, **kwargs):
"Set the unique root object. Call only once."
kwargs['left_visit'] = 1
kwargs['right_visit'] = 2
rootObj = cls(**kwargs)
rootObj.save()
return rootObj
def getAncestors(self):
"Returns all ancesters of this node."
return self.objects.filter(
left_visit__lte=self.left_visit,
right_visit__gte=self.right_visit,
).order_by('left_visit')
def getSubtree(self):
"Returns the subtree under (and including) this node."
return self.objects.filter(
left_visit__gte=self.left_visit,
right_visit__lte=self.right_visit,
).order_by('left_visit')
def addChild(self, **kwargs):
"Adds a child node under this node."
cls = self.__class__
nodeCount = cls.getRoot().right_visit
parent_left_visit = self.left_visit
parent_right_visit = self.right_visit
cursor = connection.cursor()
cursor.execute("""
UPDATE %s SET right_visit = right_visit + 2
WHERE right_visit >= %s
""", (cls._meta.db_table, parent_right_visit))
cursor.execute("""
UPDATE %s SET left_visit = left_visit + 2
WHERE left_visit > %s
""", (cls._meta.db_table, parent_right_visit))
kwargs['left_visit'] = parent_right_visit
kwargs['right_visit'] = parent_right_visit + 1
newNode = cls(**kwargs)
newNode.save()
self.right_visit += 2
return newNode
def __cmp__(self, rhs):
return cmp(self.left_visit, self.right_visit)
def leaves(self):
"Returns all leaf nodes under this node."
return self.getSubtree().extra(where=['left_visit + 1 = right_visit'])
Comments
django-mptt does all that for you (and more!)
http://code.google.com/p/django-mptt/
#
Ah, ok, it looks like they've done a very thorough job. I just pulled this out of my code after I'd cleaned it up to use model inheritance. Perhaps it's still useful as a very small subset of what django-mptt does. Thanks for the link.
#