Dynamic URLs in Django

I long ago switched my company’s web applications from Code Igniter to Django. The main reasons were Django’s more powerful database API and Python’s maintainability and scalability over PHP.

The only feature of Code Igniter that I really miss in Django is the ability to add a page without first registering the url, formulating a regular expression to describe the parameters and variations of the view function, and then creating the view and template.

For a production website, this is a better method than Code Igniter’s strategy of http://domain.com/class/method/arg/arg/…/, but CI’s is much easier during development. My urls may change half a dozen (or more) times in the course of the project, especially if marketing or advertising is involved.

Fortunately, there is an easy fix. A generic, introspective function and a carefully crafted pattern in urls.py can save the day. The url pattern is simple enough:

(r'^/path/to/app/root/(?P.+?)/(?:(?P.+?)/)?$', free_pattern)

Placing this at the end of your urlpatterns tuple will allow any undefined urls to go through this as a last-chance catch-all. It captures the view as first segment (after the application’s root path) as well as any other text afterward (leaving out leading and trailing slashes).

Here is the function:

def free_pattern(request, view, segments=""):
    segments = segments.split(r'/')
    try:
        view_fn = getattr(PATH_TO_APP, view)
    except AttributeError:
        raise Http404
    return view_fn(request, *segments)

The function is simple enough. It accepts the request, the view’s name, and the rest of the segments. The segments are split by the forward slash to turn them into a list.

If the view is found in the appropriate module (defined using PATH_TO_APP, which should point to whichever application we are talking about), it is run with *segments expanded as its arguments. If the view is not found, we raise an Http404 error.

This could easily be expanded to introspect the path to the application, but since Django supports a number of different layouts for applications and projects, that will most likely be very customized to your setup.

For example, I typically store my applications in an apps module, each with its own urls.py file that is included at the project level. That way, an update to an application does not require synchronizing changes across multiple projects.

Leave a comment | Trackback
Nov 12th, 2007 | Posted in Tips
Tags: ,
  1. Feb 20th, 2010 at 04:49 | #1

    Thanks much, I’m working on my first Django site right now and was wondering about creating this kind of catch-all. Simple and does the job.

    Kyle

  2. floepi
    Jun 15th, 2010 at 19:01 | #2

    Hi there,

    I am trying to make this work with Django 1.2 and always get an error:

    unknown specifier: ?P.

    My urls.py looks like

    from django.conf.urls.defaults import *

    def free_pattern(request, view, segments=”"):
    segments = segments.split(r’/')
    try:
    view_fn = getattr(‘/’, view)
    except AttributeError:
    raise Http404
    return view_fn(request, *segments)

    urlpatterns = patterns(‘mysite.controllers’,
    (r’^people/$’, ‘people.index’),
    (r’^(?P.+?)/(?:(?P.+?)/)?$’, free_pattern)
    )

    Could you please tell me what I am doing wrong.

    Thanks for posting this solution by the way.

    Cheers,

    Phil

  3. Dave Holden
    Aug 5th, 2010 at 04:29 | #3

    For simple HMTL pages you could always use the FlatPages app that comes with DJango:-

    http://docs.djangoproject.com/en/dev/ref/contrib/flatpages/

    Or, a generic method would have the urls.py such as

    urlpatterns = patterns(‘mysite.controllers’,
    (r’^people/$’, ‘people.index’),
    (r’^(?P[^/]+)/(?P[^/]+)/$’, ‘generic_view’)
    )

    the generic_view then loads a specific view if it exists, else it uses a simple generic handler.