Archive for October, 2008

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.

Creating a view for the menu

October 10, 2008

I now have a very basic menu in my dummy application. Now I want to display it to the end user, which is what the 3rd part of the Django tutorial covers.  The tutorial starts off with desiging the URLs that the end user will see. Well for the end user what I am creating is essentially a website. So my URLs will be something like:

http://www.example.com/node1/node1.1

etc. Except that node1 and node1.1 will be meaningful titles such as “About us” and “Staff”.  These could be structured to contain the following sort of URL:

http://www.example.com/about-us/staff

The system should then bring back the menu expanded to the correct sub menu and with the correct menu item selected.  Thus my URLs are quite straightforward in one sense and I will only need one regular expression pattern for now (essentially matching everything after the domain name). But I also need to figure out how to then split the path into it’s component sub-sections. Ideally I would like to end up with a urlpattern that results in a call which looks something like this:

view_menu(request=<HttpRequest object>, subsection1='about-us', subsection2='staff', subsection3=...)

And that there is no limit to the depth of the tree structure. The easier option is to just do this:

view_menu(request=<HttpRequest object>, path='about-us/staff/')

and then work out the subsection details in the view_menu function. I also think that if I store the path with the node in the database then this would be much more efficient (although much more complex to keep up to date).  So going with the latter idea, my url pattern will be (essentially matching anything for now!):

(r'^(?P.*)/$', 'contentedweb.cwcms.views.menu')

Now I need to code up the corresponding view for this URL pattern. But in order to keep the code in my public facing views clean and nimble (as these are the ones that are going to be called most often) I need to make a change to the node model to include the path to the node:

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)

    def __unicode__(self):
        return self.title

So I have added the path field to the model. For now I will make do with having to populate this field manually, but further down the line this field will be populated and maintained by the model not by the user creating the menu.

Next I need to bring back the relevant part of the menu from the database and display it in HTML indicating which menu item is selected. So I created a basic menu in the admin site and came up with this view (well essentially it’s copied from the tutorial…):

from django.shortcuts import render_to_response
from contentedweb.cwcms.models import Menu, Node

def menu(request, path):
    main_menu = Menu.objects.get(id=1)
    main_menu_items = Node.objects.filter(menu=1, parent=None)

    return render_to_response('cwcms/menu.html', {'main_menu': main_menu,'main_menu_items': main_menu_items,'path': path})

and this template:

<html>
<body>
{% if main_menu %}
    <h1>{{ main_menu.title }}</h1>
    <ul>
    {% for node in main_menu_items %}
        <li{% ifequal node.path path %} class="selected"{% endifequal %}><a href="/{{ node.path }}">{{ node.title }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>Menu could not be found</p>
{% endif %}

</body>
</html>

Creating this, admittedly very basic, template was straight forward. There were only two problems I encountered. Firstly, I had tried to use the filter method to return the main menu info:

main_menu = Menu.objects.get(id=1)

But then the template didn’t work because main_menu was not actually a “Menu” object, but a list of “Menu” objects containing one element in the list. Secondly I was trying to use something like this in the templating system:

{% if node.path == path %}

Which threw an error because the templating system uses slightly different control flow statements. The correct form for the above code is:

{% ifequals node.path path %}

I am not sure why the templating system should use a different set of statements in this respect, but anyway, the documentation quickly came to the rescue.

Now I need to cope for the case that the path does not exist and raise a 404 error. So I modified the view to include a try-except construct:

    try:
        main_menu = Menu.objects.get(id=1)
        main_menu_items = Node.objects.filter(menu=1, parent=None)
        node = get_object_or_404(Node, path=url_path)
    except Node.DoesNotExist:
        raise Http404

Which basically checks that the path being looked for actually exists in the database. Adding in the 404 template is also straight forward. All you need to do is create a 404.html template in the root of the templates folder and Django will handle the rest.

Well I now have a very basic menu system in place. Obviously there is lots to do but right now I am concentrating on working myself through the tutorials. Once I have covered all 4 I will start expanding on my little app.