Changes between Initial Version and Version 1 of ModifiedPreorderTreeTraversal


Ignore:
Timestamp:
Feb 1, 2006, 5:59:32 AM (19 years ago)
Author:
inerte@…
Comment:

Adding ModifiedPreorderTreeTraversal

Legend:

Unmodified
Added
Removed
Modified
  • ModifiedPreorderTreeTraversal

    v1 v1  
     1There's a good article explaining [http://www.sitepoint.com/print/hierarchical-data-database what's Modified Preorder Tree Traversal on SitePoint]. Basically, it allows you to discover every children, or parent, or the whole tree, with a single query. Very efficient! And good for threaded discussions...
     2
     3I modified these snippets from an app I coded in portuguese, with more features than what's pasted here. If there's anything wrong, send me an email, because I do have it working here :)
     4
     5Here's a model, a view and a couple functions to help you implement it.
     6
     7'''Model'''
     8
     9What you really need are the ''lft'' and ''rght'' "columns". Yes, they mean ''left'' and ''right'', but these are reserved SQL words. Everything else is optional. You will probably want an "article" or "post" fields, using ForeignKey, to say where the comment belongs to. If you do add something like this, don't forget to change the SQL queries to add the column.
     10
     11{{{
     12class Comment(meta.Model):
     13    body = meta.TextField()
     14    lft = meta.PositiveIntegerField(db_index = True)
     15    rght = meta.PositiveIntegerField(db_index = True)
     16}}}
     17
     18== New record ==
     19
     20I have this on my request (form) processing view, but you can put it anywhere (_pre_save() it's a good idea). The ''comment_id'' pk is the comment we're replying to, the one that will become the parent:
     21
     22{{{
     23comment = get_object_or_404(comments, pk = comment_id)
     24
     25target_rght = comment.rght - 1
     26
     27new_comment_lft = target_rght + 1
     28new_comment_rght = target_rght + 2
     29   
     30cursor = db.cursor()   
     31
     32cursor.execute("UPDATE `app_comments` " \
     33               "SET `rght` = `rght` + 2 " \
     34               "WHERE `rght` > %i" % int(target_rght))
     35
     36cursor.execute("UPDATE `app_comments` " \
     37               "SET `lft` = `lft` + 2 " \
     38               "WHERE `lft` > %i" % int(target_rght)
     39
     40db.commit()
     41
     42new_comment = comments.Comment(body = some_var
     43                              lft = new_comment_lft,
     44                              rght = new_comment_rght,
     45                              )
     46
     47new_comment.save()
     48}}}
     49
     50
     51== Getting the tree ==
     52
     53{{{
     54comment_list = comments.get_list(order_by=['lft'])
     55
     56stack = []
     57for comment in comment_list:
     58    # [:] creates a copy of the list, needed because .pop() modifies it on each iteration and mess with the loop
     59    stack_copy = stack[:]
     60    for j in stack_copy:
     61        if j < comment.rght:
     62            stack.pop()                   
     63
     64    stack_size = len(stack)
     65
     66    comment.stack = stack_size       
     67
     68    stack.append(comentario.rght)
     69}}}
     70
     71So, what have we done? There's a "stack" attribute on each comment now that tells you "how far" each comment is from another. For example, every "root" comment has a stack of 1. Every comment that's an answer to the root comments has a stack of 2. The stack respect the order that the comments were inserted at the database. Something like this:
     72
     73{{{
     741
     75 2
     76  3
     77  3
     78 2
     79
     801
     81 2
     82  3
     83}}}
     84
     85If you want to show the comments a little far from the left viewport border, based on their stack numbers, use this on your template:
     86
     87{{{
     88<div style="margin-left:{% widthratio comentario.stack 20 100 %}%">
     89  {{ comment.body }}
     90</div>
     91
     92}}}
     93
     94The same can be done but shrinking a table. It gets smaller, aligned to the right, the further the comment is stacked:
     95
     96{{{
     97<table style="width:{% widthratio comentario.stack 500 100 %}%" align="right">
     98  <tr><td>
     99  {{ comment.body }}
     100  </td></tr>
     101</div>
     102}}}
Back to Top