Archive for the ‘hierarchy’ Category

Doing my own thing in Django

October 11, 2008

I have so far followed the first three parts of the Django tutorial. These have been very helpful in getting me started with Django. The fourth part of the tutorial covers form processing and generic views. Having read through the tutorial it is clear to me that neither of these are of immediate interest to me. So for now I am going to go ahead and start doing my own thing.

For now I want to concentrate on making it as easy as possible for a user to create and modify a menu structure (or a taxonomy structure). My one chief gripe with many of the CMSs I’ve experimented with is how difficult it is to create and maintain large menu systems. I don’t claim to have the answers, but I want to experiment a little with this. Rather than having to add in each menu item, one by one I would like to offer users the ability to import them from essentially any kind of format, eg: XML, CSV, YAML or a simple indented text file. For example a menu could be created in an HTML text area using the following structure:


Home
About Us
  Contact Us
  Staff
Products
  Widget 1
    Overview
    Details
  Widget 2
Services
  Service 1
  Service 2

I am hoping that processing something like this is straight forward in Python as the language itself requires correct indentation. You could further enhance this with a +/- qualifier to indicate whether the node is visible or not (a common menu structure requirement) and also some for of indicating what the URL path of the node should be (if it is different to the node title), eg:


Home
About Us
  -Contact Us
  +Staff
Products
  Widget 1 | widget-one
    Overview
    Details
  Widget 2 | widget-two
Services
  Service 1
  Service 2

In the above menu the “Contact Us” node would not be displayed in a menu whereas the “Staff” node and all the other nodes would be. I have added the plus to “Staff” for illustration purposes, but the + can be implied. Also the URL of the node “Widget 1″ would be “http://www.example.org/products/widget-one/”, whereas the one of “Staff” might default to “http://www.example.org/about-us/staff/”.

In order to get going with this, I need to add a couple of fields to the node model:

    position = models.CharField(max_length=1000)
    parents = models.CharaField(max_length=1000)

Both these fields are comma delimited strings. The “position” field stores the overall position of the node in the menu. So for the “Staff” node above, the position field might be “01,01″. By padding to the left with 0s it means I can order the nodes by the position field and get the entire menu back in one query with the correct hierarchy intact. The number of digits has to be consistent for the ordering to work, so the above example allows for up to 100 child nodes for each node. Whilst this is a restriction, if you need more than 100 child nodes in your menu, you should probably consider redesigning the menu structure!

The other field stores all the parent ids of the node. So the parents field of the “Details” page of “Widget 1″ might be something like this: “5,6″ (assuming the nodes were added in the order listed and “Products” therefore has the id 5). You might also want to include the id of the “Details” page itself. It doesn’t matter as long it is consistent.

I have used both these fields in previous work and find them a very efficient way of maintaining info about a hierarchy. They are however quite complex to maintain as you have to update them throughout the menu whenever you move an item. Still, in most scenarios menus are not changed much once a site goes live so it is more important that viewing the menu hierarchy is quick rather than maintaining it.

Using these two fields, there is no real need for the “parent” and “sorting” field. However I like to keep them in as they make certain queries more straightforward. They are also useful as a backup in case something goes wrong with the “parents” and “position” data.

My node model now looks something like this:

class Node(models.Model):
    title = models.CharField(max_length=100)
    sort_order = models.IntegerField(default=1)
    menu = models.ForeignKey('Menu')
    parent = models.ForeignKey('self', blank=True, null=True)
    is_visible = models.BooleanField(default=True)
    path = models.CharField(max_length=1000)
    position = models.CharField(max_length=1000)
    parents = models.CharField(max_length=1000)

    def __unicode__(self):
        return self.title

The next entry will deal with how to maintain these two extra fields without the end user having to know about them.

If any readers have other ideas on how to maintain a hierarchy I would be very interested in hearing them. I know the above solution is very common but I also believe there are probably more efficient methods of storing hierarchical information in a database.