Multiple Templates in Django

0
210
12 min read

Considering the different approaches

Though there are different approaches that can be taken to serve content in multiple formats, the best solution will be specific to your circumstances and implementation.

Almost any approach you take will have maintenance overhead. You’ll have multiple places to update when things change. As copies of your template files proliferate, a simple text change can become a large task.

Some of the cases we’ll look at don’t require much consideration. Serving a printable version of a page, for example, is straightforward and easily accomplished. Putting a pumpkin in your site header at Halloween or using a heart background around Valentine’s Day can make your site seem timely and relevant, especially if you are in a seasonal business.

Other techniques, such as serving different templates to different browsers, devices, or user-agents might create serious debate among content authors. Since serving content to mobile devices is becoming a new standard of doing business, we’ll make it the focus of this article.

Serving mobile devices

The Mobile Web will remind some old timers (like me!) of the early days of web design where we’d create different sites for Netscape and Internet Explorer. Hopefully, we take lessons from those days as we go forward and don’t repeat our mistakes. Though we’re not as apt to serve wholly different templates to different desktop browsers as we once were, the mobile device arena creates special challenges that require careful attention.

One way to serve both desktop and mobile devices is a one-size-fits-all approach. Through carefully structured and semantically correct XHTML markup and CSS selectors identified to be applied to handheld output, you can do a reasonable job of making your content fit a variety of contexts and devices.

However, this method has a couple of serious shortcomings. First, it does not take into account the limitations of devices for rich media presentation with Flash, JavaScript, DHTML, and AJAX as they are largely unsupported on all but the highest-end devices. If your site depends on any of these technologies, your users can get frustrated when trying to experience it on a mobile device.

Also, it doesn’t address the varying levels of CSS support by different mobile devices. What looks perfect on one device might look passable on another and completely unusable on a third because only some of the CSS rules were applied properly. It also does not take into account the potentially high bandwidth costs for large markup files and CSS for users who pay by the amount of data transferred. For example, putting display: none on an image doesn’t stop a mobile device from downloading the file. It only prevents it from being shown.

Finally, this approach doesn’t tailor the experience to the user’s circumstances. Users tend to be goal-oriented and have specific actions in mind when using the mobile web, and content designers should recognize that simply recreating the desktop experience on a smaller screen might not solve their needs. Limiting the information to what a mobile user is looking for and designing a simplified navigation can provide a better user experience.

Adapting content

You know your users best, and it is up to you to decide the best way to serve them. You may decide to pass on the one-size-fits-all approach and serve a separate mobile experience through content adaptation.

The W3C’s Mobile Web Initiative best practices guidelines suggest giving users the flexibility and freedom to choose their experience, and provide links between the desktop and mobile templates so that they can navigate between the two. It is generally not recommended to automatically redirect users on mobile devices to a mobile site unless you give them a way to access the full site.

The dark side to this kind of content adaptation is that you will have a second set of template files to keep updated when you make site changes. It can also cause your visitors to search through different bookmarks to find the content they have saved.

Before we get into multiple sites, let’s start with some examples of showing alternative templates on our current site.

Setting up our example

Since we want to customize the output of our detail page based on the presence of a variable in the URL, we’re going to use a view function instead of a generic view.

Let us consider a press release application for a company website. The press release object will have a title, body, published date, and author name.In the root directory of your project (in the directory projects/mycompany), create the press application by using the startapp command:

$ python manage.py startapp press

This will create a press folder in your site. Edit the mycompany/press/models.py file:

from django.db import models

class PressRelease(models.Model):
title = models.CharField(max_length=100)
body = models.TextField()
pub_date = models.DateTimeField()
author = models.CharField(max_length=100)

def __unicode__(self):
return self.title

Create a file called admin.py in the mycompany/press directory, adding these lines:

from django.contrib import admin
from mycompany.press.models import PressRelease

admin.site.register(PressRelease)

Add the press and admin applications to your INSTALLED_APPS variable in the settings.py file:

INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.admin',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'mycompany.press',
)

In the root directory of your project, run the syncdb command to add the new models to the database:

$ python manage.py syncdb

We will be prompted to create a superuser, go ahead and create it. We can access the admin site by browsing to http://localhost:8000/admin/ and add data.

Create your mycompany/press/urls.py file as shown:

urlpatterns = patterns('',
(r'detail/(?P<pid>d+)/$',
'mycompany.press.views.detail'),
(r'list/$','django.views.generic.list_detail.object_list',
press_list_dict),
(r'latest/$','mycompany.press.views.latest'),
(r'$','django.views.generic.simple.redirect_to',
{'url': '/press/list/'})
)

In your mycompany/press/views.py file, your detail view should look like this:

from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.template import loader, Context
from mycompany.press.models import PressRelease

def detail(request, pid):
'''
Accepts a press release ID and returns the detail page
'''
p = get_object_or_404(PressRelease, id=pid)
t = loader.get_template('press/detail.html')
c = Context({'press': p})
return HttpResponse(t.render(c))

Let’s jazz up our template a little more for the press release detail by adding some CSS to it. In mycompany/templates/press/detail.html, edit the file to look like this:

<html>
<head>
<title>{{ press.title }}</title>
<style type="text/css">
body {
text-align: center;
}
#container {
margin: 0 auto;
width: 70%;
text-align: left;
}
.header {
background-color: #000;
color: #fff;
}
</style>
</head>
<body>
<div id="container">
<div class="header">
<h1>MyCompany Press Releases</h1>
</div>
<div>
<h2>{{ press.title }}</h2>
<p>
Author: {{ press.author }}<br/>
Date: {{ press.pub_date }}<br/>
</p>
<p>
{{ press.body }}
</p>
</div>
</body>
</html>

Start your development server and point your browser to the URL http://localhost:8000/press/detail/1/. You should see something like this, depending on what data you entered before when you created your press release:

Django 1.0 Template Development

If your press release detail page is serving correctly, you’re ready to continue.

Remember that generic views can save us development time, but sometimes you’ll need to use a regular view because you’re doing something in a way that requires a view function customized to the task at hand. The exercise we’re about to do is one of those circumstances, and after going through the exercise, you’ll have a better idea of when to use one type of view over another.

Serving printable pages

One of the easiest approaches we will look at is serving an alternative version of a page based on the presence of a variable in the URL (aka a URL parameter). To serve a printable version of an article, for example, we can add ?printable to the end of the URL.

To make it work, we’ll add an extra step in our view to check the URL for this variable. If it exists, we’ll load up a printer-friendly template file. If it doesn’t exist, we’ll load the normal template file.

Start by adding the highlighted lines to the detail function in the mycompany/press/views.py file:

def detail(request, pid):
'''
Accepts a press release ID and returns the detail page
'''
p = get_object_or_404(PressRelease, id=pid)

if request.GET.has_key('printable'):
template_file = 'press/detail_printable.html'
else:
template_file = 'press/detail.html'

t = loader.get_template(template_file)
c = Context({'press': p})
return HttpResponse(t.render(c))

We’re looking at the request.GET object to see if a query string parameter of printable was present in the current request. If it was, we load the press/detail_printable.html file. If not, we load the press/detail.html file. We’ve also changed the loader.get_template function to look for the template_file variable.

To test our changes, we’ll need to create a simple version of our template that only has minimal formatting. Create a new file called detail_printable.html in the mycompany/templates/press/ directory and add these lines into it:

<html>
<head>
<title>{{ press.title }}</title>
</head>
<body>
<h1>{{ press.title }}</h1>
<p>
Author: {{ press.author }}<br/>
Date: {{ press.pub_date }}<br/>
</p>
<p>
{{ press.body }}
</p>
</body>
</html>

Now that we have both regular and printable templates, let’s test our view.Point your browser to the URL http://localhost:8000/press/detail/1/, and you should see our original template as it was before. Change the URL to http://localhost:8000/press/detail/1/?printable and you should see our new printable template:

Django 1.0 Template Development

Creating site themes

Depending on the audience and focus of your site, you may want to temporarily change the look of your site for a season or holiday such as Halloween or Valentine’s Day. This is easily accomplished by leveraging the power of the TEMPLATE_DIRS configuration setting.

The TEMPLATE_DIRS variable in the settings.py file allows you to specify the location of the templates for your site. Also TEMPLATE_DIRS allows you to specify multiple locations for your template files.

When you specify multiple paths for your template files, Django will look for a requested template file in the first path, and if it doesn’t find it, it will keep searching through the remaining paths until the file is located.

We can use this to our advantage by adding an override directory as the first element of the TEMPLATE_DIRS value. When we want to override a template with a special themed one, we’ll add the file to the override directory. The next time the template loader tries to load the template, it will find it in the override directory and serve it.

For example, let’s say we want to override our press release page from the previous example. Recall that the view loaded the template like this (from mycompany/press/views.py):

template_file = 'press/detail.html'
t = loader.get_template(template_file)

When the template engine loads the press/detail.html template file, it gets itfrom the mycompany/templates/ directory as specified in the mycompany/settings.py file:

TEMPLATE_DIRS = (
'/projects/mycompany/templates/',
)

If we add an additional directory to our TEMPLATE_DIRS setting, Django will look in the new directory first:

TEMPLATE_DIRS = (
'/projects/mycompany/templates/override/’,
'/projects/mycompany/templates/',
)

Now when the template is loaded, it will first check for the file /projects/mycompany/templates/override/press/detail.html. If that file doesn’t exist, it will go on to the next directory and look for the file in /projects/mycompany/templates/press/detail.html.

If you’re using Windows, use the Windows-style file path c:/projects/mycompany/templates/ for these examples.

Therein lies the beauty. If we want to override our press release template, we simply drop an alternative version with the same file name into the override directory. When we’re done using it, we just remove it from the override directory and the original version will be served (or rename the file in the override directory to something other than detail.html).

If you’re concerned about the performance overhead of having a nearly empty override directory that is constantly checked for the existence of template files, we should consider caching techniques as a potential solution for this.

Testing the template overrides

Let’s create a template override to test the concept we just learned. In your mycompany/settings.py file, edit the TEMPLATE_DIRS setting to look like this:

TEMPLATE_DIRS = (
'/projects/mycompany/templates/override/',
'/projects/mycompany/templates/',
)

Create a directory called override at mycompany/templates/ and another directory underneath that called press. You should now have these directories:

/projects/mycompany/templates/override/
/projects/mycompany/templates/override/press/

Create a new file called detail.html in mycompany/templates/override/press/ and add these lines to the file:

<html>
<head>
<title>{{ press.title }}</title>
</head>
<body>
<h1>Happy Holidays</h1>
<h2>{{ press.title }}</h2>
<p>
Author: {{ press.author }}<br/>
Date: {{ press.pub_date }}<br/>
</p>
<p>
{{ press.body }}
</p>
</body>
</html>

You’ll probably notice that this is just our printable detail template with an extra “Happy Holidays” line added to the top of it.

Point your browser to the URL http://localhost:8000/press/detail/1/ and you should see something like this:

Django 1.0 Template Development

By creating a new press release detail template and dropping it in the override directory, we caused Django to automatically pick up the new template and serve it without us having to change the view. To change it back, you can simply remove the file from the override directory (or rename it).

One other thing to notice is that if you add ?printable to the end of the URL, it still serves the printable version of the file we created earlier.

Delete the mycompany/templates/override/ directory and any files in it as we won’t need them again.

LEAVE A REPLY

Please enter your comment!
Please enter your name here