Tips and tricks
In a few days it’ll be the weekend and I’ll (hopefully) have a bit of news regarding updates to the various applications I spun out during the process of rewriting all the code I’m using here, as well as the first official release of the blogging application I’ve developed (you can probably already find it if you know where to look, but for now it’s neither documented nor supported). In the meantime, there are a few minor and miscellaneous — but still useful — tricks I’ve started using which are worth giving some attention to; they’re not as cool as an entire packaged application, but anything that makes life easier for a developer, even by a little bit, is a good thing.
Learn and love reverse()
One of the less noticeable changes I made was to the structure and appearance of my URLs; I had a somewhat picky system of deciding when to apply a trailing slash, and I was using numeric months in the URLs for date-based archives and entry/link detail pages. So, for example, the URL for the last entry I posted before the relaunch started out looking like this:
http://www.b-list.org/weblog/2007/07/05/quick-survey
The first change is that now everything has a trailing slash. Django’s CommonMiddleware, when APPEND_SLASH is True
, takes care of that automatically and issues a redirect to the URL with the trailing slash (I don’t want to have two URLs which potentially resolve to each entry: canonical, disambiguated URLs are a good thing). But the second change is trickier: I switched to using a textual representation for months — jan
, feb
, mar
, etc. — and so the URL of that entry now looks like this:
http://www.b-list.org/weblog/2007/jul/05/quick-survey/
Of course, that means I need to issue redirects from the old style to the new; people have linked to those old entries and search engines have indexed them, and I can’t just have them suddenly start throwing 404s. One solution would be to write a set of views which take in a numeric year, month, day and slug, look up the corresponding object and issue a redirect based on its get_absolute_url
method, but this had the disadvantage of requiring an extra database lookup every time someone hit an old-style URL (to fetch the object and call get_absolute_url
to get the URL to redirect to).
But Django provides a cleaner solution: the utility method “reverse()”, which can — given the name of a view function or a named URL pattern and a set of values for its arguments — scan your URLConf and return a string: the URL which will route to that view with those arguments.
Given reverse
and some named URL patterns, I was able to write three very short views which handle redirecting from the old-style URLs to the new. After I wrote that and posted it, I changed my mind about URLs for links — I ditched their detail and daily archive pages, opting for anchors within a monthly archive — but the principle stays the same: using reverse
and a dictionary to map numeric months to their correpsonding strings, I got dead simple redirection without having to do any extra database lookups.
And there’s another advantage to this method: instead of returning hard-coded strings from get_absolute_url
, I’m now using the “permalink” decorator, which (like reverse
) scans your URLConf looking for a view and a particular argument signature, and uses them to dynamically construct the URL to return. Between permalink
and reverse
I’m now specifying URLs in one and only one place: my URLConf. This cuts down on needless repetition (a nice nod to the DRY principle), and gave me a little more flexibility to experiment with URL schemes when I was redesigning; since both reverse
and permalink
build the URL dynamically, their output would automatically update any time I made a change in my URLConf.
The {% url %}
tag
Closely related to the problem of redirects and canonical URLs for various obejcts is finding a maintainable way to output links in templates. For this I started making very heavy use of the “url” tag, which works in basically the same manner as reverse
but is implemented as a template tag. Pretty much all the internal links you’ll see on this site (basically, anything that isn’t the output of some object’s get_absolute_url
method) comes from using {% url %}
. For example, the navigation tabs at the top of the page are implemented like so:
<ul id="main-nav"> <li id="nav_weblog"><a href="{% url b_list_home %}">Weblog</a></li> <li id="nav_links"><a href="{% url coltrane_link_archive_index %}">Links</a></li> <li id="nav_contact"><a href="{% url contact_form %}">Contact</a></li> <li id="nav_about"><a href="/about/">About</a></li> </ul>
Only the “about” page doesn’t use the url
tag, and only because it points at a flat page instead of a named URL. Doing it this way means — again — that I had some room to play with alternative URL configurations, and making a change in the URLConf automatically caused everything else to be updated (since url
, like reverse
and permalink
, scans the URLConf to figure out how to build the correct URL).
Beautiful code highlighting
Previously I’d been using a JavaScript-based syntax highlighter to handle code samples posted in my blog entries, and while it wasn’t bad it did have a couple of limitations:
- It required JavaScript to be enabled in a visitor’s browser before it could do anything.
- Because it was JavaScript and ran client-side, it meant I couldn’t store a highlighted version of the code to save re-generating it on every page view.
-
The HTML it required (in the older version I was using) wasn’t exactly appropriate; code samples had to be placed inside
textarea
elements (it can now work with apre
element instead).
What I really wanted was something written in Python which could locate and highlight code samples when an entry was saved, and it didn’t take long to come up with one. I can’t take any credit for the implementation — the code I started from was posted to djangosnippets by someone who never supplied their full name and so remains anonymous — but I did clean it up a bit to arrive at the final version I’m now using, which handles:
- Separating out the code blocks and highlighting them (using BeautifulSoup and Pygments).
- Running Markdown and typogrify over the non-code portions of the text.
For ultimate ease of use, I also tweaked it to be registered as a markup filter with the formatter from template_utils. So far I’m pretty happy with the results it produces, and above all with the way that it just triggers off raw code
elements in my blog entries.
More markup tricks
There were several cases where I had templates containing chunks of text which needed to be HTML, but which would be tedious to mark up by hand and even more tedious to properly format in the same style as typogrify
(which performs all sorts of typographic niceties). I’d pretty much resigned myself to doing this anyway, until I remembered one of Django’s most-overlooked and yet incredibly powerful built-in template tags: filter. Yes, the name of the tag is filter
; it’s a tag, not a filter. It takes the name of a template filter as its argument (with optional arguments; you can also chain filters together), and applies that to the output of everything between {% filter %}
and {% endfilter %}
. This means that, for example, you can do the following (assuming you have django.contrib.markup installed):
{% load markup %} {% filter markdown %} Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Fusce pulvinar erat ac eros. Maecenas sagittis auctor urna. Ut in arcu vel neque suscipit porttitor. Nunc in elit ut nibh aliquam mollis. Nam quis metus. Pellentesque id tellus quis dolor varius commodo. Nunc dignissim odio. Duis adipiscing. Donec pretium, erat sit amet vestibulum bibendum, risus mi pellentesque quam, ac viverra leo mauris ut est. Nulla turpis nunc, lacinia ac, sollicitudin facilisis, faucibus in, nunc. Proin sit amet odio nec lorem dignissim posuere. Pellentesque vitae risus quis tortor tempus ullamcorper. {% endfilter %}
The text between {% filter markdown %}
and {% endfilter %}
will have Markdown formatting automatically applied. I chained together Markdown and a couple typographically-oriented filters (not the all-singing, all-dancing, code-highlighting beast I use for blog entries, because it’s pretty heavyweight and would slow things down too much if it ran on every page view), and had an easy way to drop nicely-formatted HTML into strategic locations in my templates.
Sometimes it’s the little things
Again, I realize there’s nothing particularly earth-shatteringly important going on here, but these sorts of little tricks very quickly add up and make development far less tedious and fare more enjoyable than it would be otherwise. Hopefully you’ll be able to get a little bit of mileage out of at least one or two of them; and if you’ve got any favorite tips of your own, feel free to share in a comment here, over at djangosnippets or on your own blog. Whether you’re completely new to Django or an old hand, there’s always going to be some useful function or technique you can learn that’ll make your job just a little bit easier (for example, I’m greatly indebted to Collin for explaining the “permalink” decorator way back when it was a new feature in Django), and when you find it the world will want to hear about it.