At del.icio.us (and some other sites, but I'll use del.icio.us as my example), one of the most interesting features is that your username is one of the first-level "commands" you can send to the application: "http://del.icio.us/elfsternberg" is a valid URL, and points to my collection of bookmarks.

At the same, there are other first-level commands you might send to del.icio.us, including /save, /tag, /user, and /network, and /url. In Django, all of these commands would be rooted in url.py.

I'm going to show you a few very simple ways to make the magic of del.icio.us work in Django, and (most importantly) show how to validate new users at registration time to make sure that their usernames do not mask, and are not masked by, your otherwise RESTful URLs.

Using the Resolver to ensure uniqueness

I'm going to assume you know enough about Django to set up a demonstration sever. If you don't, please consult the Django documentation at the Django Project.

The basic premise here is that you have some basic RESTFUL paths, and then you have your username-based paths. Let's assume you're using django-registration, since the real difficulty here is ensuring no invalid slugs get past your registration engine. For my purposes, the project is named "demonstration" and the application "demo". Here is what you add to urls.py

<dfn><urls.py>=</dfn>
from django.conf.urls.defaults import *
from django.contrib.auth.views import *
from django.core.urlresolvers import reverse

# Just two pages to show what we mean.
from demo.index import intro, index

# This is explained below.
from demo.forms import NewRegistrationForm

urlpatterns = patterns(",
    # Ordinary URLS understood by the system.
    url(r'^$', index, name="index"),
    url(r'^index/$', index, name="index"),
    url(r'^intro/$', intro, name="intro"),

    # Override the registration application's register method to
    # include a registration form with a URL checker.
    url(r'^accounts/register/$',
        'registration.views.register',
        { 'form_class': NewRegistrationForm },
        name = 'registration_register'),

    # URLs driven by the bookmarking application
    url(r'^(?P<user_slug>\w+)/$', user, name="user"),
    url(r'^(?P<user_slug>\w+)/(?P<object_slug>\d+)$',
        bookmark, name="addnew"),

The big keys here are the NewRegistrationForm and the URLs in which the user_slug (if you don't know what a slug is, you can consult the Django glossary at , but the short form is this: a slug is a URL-ready string version of a label, all-lowercase, alphanumeric only, with spaces either eliminated or replaced with hyphens. If my name is "Elf Sternberg", typical slugs would be elfsternberg or elf-sternberg. Using slugs instead of object IDs has become popular as a way of assisting with mnemonic URLs. For more, see "Cool URLs don't change") is exploited to find the "hub" view of the user's experience. Different views are provided at the same URL for those who are logged in and viewing their own page, and those who are viewing pages belonging to someone other than themselves.

Writing the user and bookmark slugs should be trivially easy. The question here becomes, how do you prevent users from choosing a name like "index" or "intro," names that would be masked by the command syntax, since Django resolves URLs in the order in which URL patterns appear in the urls.py file

Above, in urls.py, I mentioned that we had imported NewRegistrationForm, a newly defined object within the application. I also told django-registration to use NewRegistrationForm as the form to show when going to the registration page.

NewRegistrationForm inherits from the base registration form and adds another layer of validation: confirm for me that the username being added would not conflict with an existing URL that already resolves within our application. To do that, we just use the resolve function provided by Django, by defining our own forms file under the demo/ directory:

<dfn><forms.py>=</dfn>
from registration.forms import RegistrationForm
from django.forms import ValidationError
from django.core.urlresolvers import resolve, Resolver404
from urlparse import urlparse

class NewRegistrationForm(RegistrationForm):

    # Ensures than any usernames added will not
    # conflict with existing commands.

def clean_username(self):
        username = super(NewRegistrationForm, self).clean_username()
        try:    resolve(urlparse('/' + username + '/')[2])
        except Resolver404, e:
            return username

        raise ValidationError(_(u'This username does not create '
                                u'a valid URL.  Please choose '
                                u'another'))

After importing all the necessary tools, I create a new class and override the method clean_username, which first invokes the parent class machinery for validating the username, and then attempts to resolve that username against the existing application. If resolve throws the Resolver404 exception, then we know that the username did not resolve, and is therefore valid to use! Otherwise, we raise a validation error. I always assume the presence of a gettext handler (the underscore function) and the use of unicode inside the application.

A couple of style notes: I assume gettext is installed as the underscore function, and I enjoy exploiting Python's behavior of concatening adjacent strings into a single expression. That's what the text in the exception string is doing.

Big Fat Caveat

Make sure you have a very clear vision of your product's future evolution, and that all of the commands you anticipate providing in the future have been allocated or reserved before you let your users have their hands on the system. Users are clever and will reserve usernames that might mask commands that you'll want. Your only alternatives are to negotiate with the user for a change of name, or get out the thesaurus. Neither is terrific fun.