Django Tip: Application-Specific Templates

Today I have another Django goodie to share with you. For the past few days, I've been struggling to come up with a way to only load certain Django template tag libraries when a particular Django application is installed. There may well be other, more elegant solutions for this particular problem, but it can't hurt to add my findings to the pile.

We have several templates which need to display certain information only when a particular application (Satchmo, in this case) is installed in the site. A lot of these templates are global for our 100+ Django-powered sites, such as customized admin templates and the like. It's much easier for us to maintain our code this way, as opposed to overriding templates on a per-site basis.

The Problem

We have created template tags which allow us to render "Content A" if application foo is installed or render "Content B" if foo is not installed. This works great all the way up until you need to use template tags that are specific to foo. The reason for this is that all of the template nodes appear to be parsed before they're actually rendered. That means that if foo is not installed and one of your templates included a template tag from foo's template tag library, Django will complain because it cannot find that tag (since foo is not in your settings.INSTALLED_APPS).

I investigated several possible solutions to this, including a custom loadifapp tag. The idea was to only load a template tag library if the specified application exists in settings.INSTALLED_APPS. This proved to be an interesting and very, very hacky endeavor. In the end it didn't work, and it was taking much too long to get anywhere useful.

The Solution

The solution I came up with for this situation is to create an additional include tag. I basically copied the include tag from Django itself and hacked it a bit. The result:

from django import template
from django.core.exceptions import ImproperlyConfigured
from django.db.models import get_app
from django.template.loader_tags import ConstantIncludeNode, IncludeNode

register = template.Library()

def do_include_ifapp(parser, token):
    """
    Loads a template and renders it with the current context if the specified
    application is in settings.INSTALLED_APPS.

    Example::

        {% includeifapp app_label "foo/some_include" %}
    """
    bits = token.split_contents()
    if len(bits) != 3:
        raise TemplateSyntaxError, "%r tag takes two argument: the application label and the name of the template to be included" % bits[0]

    app_name, path = bits[1:]
    app_name = app_name.strip('"\'')
    try:
        models = get_app(app_name)
    except ImproperlyConfigured:
        return template.Node()

    if path[0] in ('"', "'") and path[-1] == path[0]:
        return ConstantIncludeNode(path[1:-1])
    return IncludeNode(path)
register.tag('includeifapp', do_include_ifapp)

The magic here is the return template.Node() if Django cannot load a particular application. This makes it so the template you would be including will not be parsed, and the invalid template tag errors disappear!

To use this tag in your Django-powered site, simple plug it into one of your template tag libraries and do something like this:

{% extends 'base.html' %}
{% load our_global_tags %}

{% block content %}
<h2>Global Content Header</h2>
Bla bla

{% includeifapp foo 'foo_specific_junk.html' %}
{% endblock %}

And within foo_specific_junk.html you would load whatever template tag libraries you need that would break your templates without foo being installed. This tag should work for any application. I would be interested to hear what you use it for in the comments!

Announcing django-ittybitty 0.1.0-pre2

I'd like to take this opportunity to officially announce my latest little side project: django-ittybitty! Some of you out there might not find this to be a useful application, but I hope others will enjoy it.

Many of you are familiar with the URL-shortening sites like http://tinyurl.com/, http://is.gd/, http://cli.gs/, and whole slew of others. These sites are all fine and dandy, right? Wrong! What happens when those sites have downtime and potential visitors to your site never get to your site because the URL-shortening site is down? You lose traffic. That's not good, in case you were unsure about it.

That is why I made this application. It allows you to have short URLs for any and every page on your Django site. No more need to rely on 3rd party servers to translate short URLs to real URLs on your site. So long as your pony-powered site is up and running, your visitors will be able to use URLs generated by this application to get anywhere on your site. All you need to do to make this work is download and install the application, add a middleware class to your MIDDLEWARE_CLASSES, and then use a simple template tag to generate a short URL for any given page.

django-ittybitty will keep track of the number of times a particular "itty bitty URL" has been used to access your site. I suppose some people will find that useful, but it's hardly a true metric for your "most popular" pages.

The algorithm behind this application is very simple, but it can potentially handle around 18,446,744,073,709,551,615 shortened URLs in 64 characters or fewer, neglecting the 'http://www.....' for your site (good luck getting your database to play well with that many records, much less storing them on a server :)).

For more information, please check out the project pages and enjoy:

For those who are interested, here are some code samples for how to use django-ittybitty:

{% extends 'base.html' %}
{% load ittybitty_tags %}

{% block content %}
<a href="{% ittybitty_url %}">Link to this page!</a>
{% endblock %}

or:

{% extends 'base.html' %}
{% load ittybitty_tags %}

{% block content %}
{% ittybitty_url as ittybitty %}
<a href="{{ ittybitty.get_shortcut }}">Link to this page!</a>
{% endblock %}

or:

{% extends 'base.html' %}
{% load ittybitty_tags %}

{% block content %}
{% ittybitty_url as ittybitty %}
{% with ittybitty.get_shortcut as short_url %}
<a href="{{ short_url }}">Link to this page!</a>
<a href="{{ short_url }}">Link to this page again!</a>
<a href="{{ short_url }}">Link to this page one more time!</a>
{% endwith %}
{% endblock %}

Enjoy!

Firebug for !Firefox

Pretty much anyone who's been doing any Web development in the last few years probably prefers to use Firefox because of the incredibly powerful extensions it offers. Among the extensions I hear most Web developers complain about not having in other browsers are Web Developer and Firebug. Several people feel that they could get by with another browser (such as Google Chrome) if it only had Firebug.

Well, my friends, the trusty folks who built Firebug actually offer their amazing product for other browsers! It goes by the name of "Firebug Lite." I'm not sure exactly how long this has been around, but the earliest date I can find suggests that it was released to the public in July of 2008.

I happened upon this utility while perusing Django Snippets the other day. A member by the username of jfw posted a middleware which injects the Firebug Lite utility into a response when you're not using Firefox and when your site is in debug mode. I've found it to be quite useful. I hope you all do too!!

jQuery 1.3.0 Released

Today the jQuery team has released version 1.3.0 of their amazing JavaScript toolkit. It boasts a lot of great improvements, such as better selector performance, "live events" (YAY!!), drastically improved delegation filtering, overhauled HTML injection functions, et cetera. I haven't yet had a chance to play with it, but I think it will be quite fun once I do. I have a lot of Django-powered apps that are running on older versions of jQuery.

For anyone who's interested in learning more, check out the jQuery 1.3.0 release notes.