Code


Version 9 (modified by anonymous, 7 years ago) (diff)

update for django 0.96

Update: This code has been updated, as described in this message on django-users: http://groups.google.com/group/django-users/browse_frm/thread/ad54ef8b858c2d33

There's a good article explaining what's Modified Preorder Tree Traversal on SitePoint. I am not going to explain the whole theory here, just offer some code so you can implement it quickly.

Basically, it allows you to discover every children, or parents, or the whole tree, with a single query. Very efficient! And good for threaded discussions...

Download the necessary files from my website. For now, it's just a model and a view (with a single function). I thought about adding a template tag but stick with the do-it-yourself flexibility.

In Django 0.96 the code given does not work; You need to change the following in models.pyc:

  1. change "from django.contrib.contenttypes.models import ContentType" to "from django.contrib.contenttypes import generic"
  2. change "content_object = models.GenericForeignKey()" to "content_object = generic.GenericForeignKey()"

If you hack it to add some functionality, drop me an email (inerte is my gmail.com username). Current version is 0.9, and if it doesn't break anything, as soon as I finish editing the forms that I use to serve as an example, it will be 1.0. And if we depend on any new features, it will probably stay at this version forever :p

If you need more documentation, check old versions from this wiki page (specially the first and second), for a longer explanation of the whole thing, when mptt wasn't in a file for you to download, but just code that I pasted here.

How to install

Drop the mptt folder inside your Django project folder.

Edit the settings.py of your project and add a 'mptt' element into the INSTALLED_APPS array.

Change to the directory of your project (where manage.py is) and type:

python manage.py install mptt
python manage.py sqlindexes mptt

If you have to, copy the output of sqlindexes and apply to your database. This is very important for performance! The object_id and lft columns should be indexed!

Using it

Edit the views.py file from your app to call mptt's views.py and pass its function to a template:

from your_project_name.mptt.views import *

# Example view
def your_view(request, id):
    your_object = get_object_or_404(models, id__exact = id)
    
    context = {'your_object': your_object,
               'node_tree': node_tree(id),
              }
    
    return render_to_response('dir/file', context)

Now we have a list called "node_tree" to use on your template. What's is it? There's a "stack" attribute on each node now that tells you "how far" each node is from the object. For example, every "root" node (the direct reply to a "post", for example) has a stack of 1. Every node that's an answer to the root nodes has a stack of 2. The stack respects the order (because of order_by=lft?) that the nodes were inserted at the database. So we end up with nodes having stacks numbered like this:

1
 2
  3
  3
   4
 2
 2

1
 2
  3
  3

Template

If you want to show the nodes a little far from the left viewport border, based on their stack numbers, use this on your template:

{% for node in node_tree %}
    <div style="margin-left:{% widthratio node.stack 20 100 %}%">
      {{ node.body }}
    </div>
{% endfor %}

The same can be done but shrinking a table. It gets smaller, aligned to the right, the further the node is stacked:

{% for node in node_tree %}
    <table style="width:{% widthratio comentario.stack 500 100 %}%" align="right">
      <tr><td>
      {{ node.body }}
      </td></tr>
    </div>
{% endfor %}

I will leave to you to code the necessary forms to insert the new node on your public view. Just remember to give a new Node the object_id which we're replying to. If it's a Node itself, the new node/comment will act as a "reply". If it's for any other Django content, it's like a new "root" node/comment.

Other than that, the whole mptt model and view aren't very smart. Editing them using the admin interface is hard and doesn't make sense (because nodes represent threaded content, and not single objects).

But, good luck!