My name is Paul Bissex, and e-scribe.com is my consulting business. I build web applications using as much open source software as possible. From September to June I teach web design and other important non-photographic professional skills to photographers. In the '90s I wrote technology commentary and reviews for magazines, newspapers, and web publications, including Wired, Salon.com, FamilyPC, the late lamented Web Review, and the Chicago Tribune. Feel free to email me.
I'm co-authoring a book, "Python Web Development with Django", with Jeff Forcier and Wesley Chun. It will be published by Prentice Hall in July 2008, but is available for pre-ordering on Amazon now.
This site is built on a fresh trunk checkout of Django, running on Python 2.5.1, served by Apache and mod_python. The database is SQLite. The operating system is FreeBSD, on a VPS hosted at Johncompanies.com. Comment-spam protection by Akismet. Vintage topo imagery from the Maptech archive.
Akismet, del.icio.us, Django, dpaste.com, Emacs, FreeBSD, Freenode, jQuery, LaunchBar, MacPorts, Markdown, Mercurial, OS X, Postfix, Python, SQLite, Subversion, TextMate, Trac, Ubuntu Linux, wmii
Copyright 2008
by Paul Bissex
and E-Scribe New Media
One of the great things about Django is its simple and flexible URL handling. If your Django work, like mine, includes converting existing sites, you'll probably be doing some URL cleanup along the way. Django's "generic views" system includes a view called "redirect_to" that handles cases like this. A rule might look like this:
urlpatterns = patterns('django.views.generic.simple',
('^foo/oldsite/BadOldUrl33247.blech$', 'redirect_to', {'url': '/bar/nice-new-url/'}),
)
But because the URL pattern building happens in Python, if you have many of these you can do better than filling your urls.py with variants of that line. Here's the root urlconf for one of my sites:
urlpatterns = patterns('',
# ...long list of patterns here
(r'^admin/', include('django.contrib.admin.urls')),
)
legacy_urls = (
('^kawasaki/zx750f', '/bikes/kawasaki/zx750f/'),
('^pages/ktm_2wd_interview', '/bikes/ktm/2wd/'),
('^gear/[0-9]+/$', '/gear/'),
)
for urltuple in legacy_urls:
oldurl, newurl = urltuple
urlpatterns += patterns('',
(oldurl, 'django.views.generic.simple.redirect_to', {'url': newurl}))
The first block is my main set of patterns. The second is my list (OK, it's a tuple) of old/new URL pairs. The third block just extends the urlpatterns object with rules for these pairs, using the generic "redirect_to" view.
I haven't needed it for this project, but the documentation explains how the redirects can also be parameterized. For example, if you needed to translate all URLs like "/foo/99/" into "/bar/99/", you'd add this pair to legacy_urls above:
('^foo/(?P<id>\d+)/$', '/bar/%(id)s/'),
Overall this approach is just minor syntax twiddling, but I find the simple tuple style easier to read and maintain.
Techniques like this may be obvious to Django users who grasp the beauty and power of its pure-Python approach -- but I hope it can be helpful for some newcomers who are used to other frameworks where configuration files are brittle, fussy things.
Very cool, Simon, thanks! I'm actually using the callable-object syntax in most of my URLconfs, but I hadn't thought to take advantage of it in that way.
Another suggestion, particularly for those with a lot of "dynamic" content (ie, converting from a major blog or cms application), is to use django.contrib.redirects. It installs just like Flatpages (add it to INSTALLED_APPS, add its middleware near flatpage's, and syncdb) and keeps your redirect information in the database. I was able to populate my redirects table with legacy URLs as a part of the database conversion of my blog. By having it in the database it makes it really easy for me to slowly phase out legacy URLs as search engines adapt and I weed out legacy links.
Comments use Markdown syntax. Your comment will not appear until approved, which may take a few hours or more. Spammers will be torpedoed.
The iPhone keyboard doesn't suck
Python one-liner of the day
7 comments
How not to advocate via Google Code
2 comments
99 problems
3 comments
bitmonk
Obscure "svn mv" problem solved
89 days ago
Charlie
Book news: Rough Cuts and Amazon
90 days ago
Simon Griffee
Django Mercurial mirror tweaks
107 days ago
Jason Calleiro
From PHP to Python
108 days ago
Yuli
dpaste.com
111 days ago
bruce
Neat Python hack: infix operators
115 days ago
David Reynolds
The original Lego Star Wars
123 days ago
At least 36719 pieces of comment spam killed since January 12th. Thanks are mostly due to Akismet.
There's an even neater way of doing this, thanks to a feature that recently went in to Django that allows you to provide views as callable objects instead of as strings: