File Under: Frameworks, Programming

Use Templates in Django

This is part 4 of Webmonkey’s introductory Django tutorial. If you’re arriving here to learn about getting started with Django, start back at the beginning with Lesson 1.

When we left off last time, we had defined some URLs for our blog and constructed a custom view to handle displaying posts by tag. If you point your browser to our development URL at this point, (http://127.0.0.1:8000/blog/) you’ll still see a Django error page complaining that the template blog/list.html does not exist. Don’t panic, it’s true — we haven’t created it yet.

It’s time to tackle the last aspect of Django, the template syntax.


Contents

  1. Create some templates
  2. Inheritance
  3. A simple template
    1. Using built-in filters
    2. Displaying tags
  4. Finishing up the template
  5. More about built-in template filters
  6. Roll your own template tags
  7. Links and other resources

Create some templates

If you look back through the code we’ve written so far, you’ll find that we’ve point Django to a number of templates (check out the [Tutorial:Use URL Patterns and Views in Django | urlpatterns code] and the custom view we wrote).

The templates we’ve defined need to be created. We’re going to use the following names within this directory structure:

djangoblog

	- templates

		-blog

			list.html

			detail.html

		-tags

			list.html

You can go ahead and create that directory structure — just create a folder in your main “djangoblog” folder and name it “templates.” Inside that, create two folders named “blog” and “tags.”

Then create your list.html and detail.html files.

Note: The .html extension is totally optional — I prefer it because it turns on the syntax highlighting feature in my text editor, but you can use any extension you like.

Inheritance

If we step back for a minute and think about our blog and what our templates need to display, the first thing that jumps out at you is that there’s a whole bunch of stuff that’s common to every page — a header, site navigation, sidebar, footer, and so on.

It would be silly (and a egregious violation of the DRY principle) if we wrote that code more than once. Luckily, like most good template languages, Django provides a way to extend a single template file. We can define our site-wide components once and then simply inherit from that file, adding in the aspects of the site that do change.

So before we dive into the detail pages, let’s first create a base file. Being the creative type, I generally call the file base.html. So inside the templates folder you created above, add a new file called base.html.

A simple template

Open that file in your text editor. Let’s sketch out some of the site-wide HTML we might need. For the sake of this tutorial, I’ve kept things pretty simple, but feel free to get as fancy as you want with your HTML. Here’s a basic starting point:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html lang="en">

	<head>

		<title>My Site - {%block pagetitle %}{% endblock %}</title>

	</head>



	<body>

		<div id="header">My Site</div>

		<div id="nav">

			<ul>

				<li><a href="/">home</a></li>



				<li><a href="/blog/">Blog</a></li>

				<li><a href="/links/">Links</a></li>

				<li><a href="/about/">About</a></li>



			</ul>

		</div>

		<div id="content">

			<div id="primary">

				<h1>{% block title %}{% endblock %}</h1>



				{% block primary %}{% endblock %}

			</div>

			<div id="secondary">

				<h3>Recent Entries:</h3>

			<div>



		</div>

	</body>

</html>

That should be pretty self explanatory, save perhaps the curious things enclosed in curly brackets, the {% %} bits. What up with that stuff?

That is Django’s template syntax for creating blocks which other templates can plug data into. In this case we’re creating a base template with a few blocks (think of them as holes) and our other templates will extend this and fill in the holes.

To see what I mean let’s create the blog/list.html template. Add a new blog folder in the templates folder (if you haven’t already) and create a file list.html. Now open that up and past in the line:

{% extends 'base.html' %}
.

Now, if you revisit http://127.0.0.1:8000/blog/ the template not found error should be gone. In its place, you should see everything we just put in base.html.

Not much, I’m afraid. But fear not — now that we’re extending base.html, we can start plugging in some values for those blocks we created earlier.

Add this below the extends statement:

{% block pagetitle %}Tumblelog{% endblock %}

{% block title %}My Page Headline{% endblock %}

{% block primary %}

{% for object in latest %}

	<h2>{{ object.title }}</h2>



	<p>{{ object.pub_date }}</p>

	 {{ object.body_html|truncatewords_html:"20"|safe }}

	<p>tags: {% for tag in object.get_tags%}<a href="/blog/tags/{{tag.name|slugify}}/">{{tag}}</a>{% endfor %}</p>

	<p><a href="{{object.get_absolute_url}}">read more</a></p>



{% endfor %}

{% endblock %}

The first thing we do is fill in our page title block in the head tags. Then we do the same for the displayed title. The next block we fill in is the primary content block. Here’s where we display the data that our generic view grabbed for us.

The Django template syntax can be considered fairly “Pythonic,” in that your define loops using the same for x in dataset syntax as Python. In this case, the generic view function object_detail passes in a variable named latest. By default, this displays the fifteen latest entries, though you can go back to the urls.py file and increase that number using the num_latest param.

All we do is construct a loop using the latest variable. Then within that loop we pull out some of our data — again, accessing an objects attributes uses the Python-like dot accessor methods.

Using built-in filters

The only part that requires additional explaining is the object.body_html section where we’ve applied two built-in Django template filters, truncatewords_html and safe.

Truncatewords_html should be fairly obvious — this clips the body of our post after twenty words, but also preserves the structure of the HTML by appending any closing tags to make sure the HTML remains intact.

The safe filter simply tells Django that it’s OK to display HTML. Without the safe filter, Django will automatically escape all HTML entities and tags. Autoescaping is a nice feature for avoiding nefarious XSS attacks and the like, but in this case, since we trust the source, we’ll let the HTML through.

Displaying tags

All fine so far, but what about tags? We have tags on our entries, so we might as well display them. This is what we do in the next line.

You’ll notice that in that line, we have a loop within a loop. Remember when we created our models and we added a get_tags method to return all the tags for each entry? Well, here it is in action.

That will loop through all the tags for each entry and display them along with a link to that tag’s permalink page. And note that we’ve used the slugify filter to make sure than any multiword tags will be hyphenated in the URL

Note: If you remember back when we wrote our custom tag view, we used a string replace function to “unslugify” the URL for lookup in the datebase.


Finishing up the template

The last line calls the get_absolute_url function that we defined when we built our model in the last lesson. This provides a link to the permalink page for each entry in the list.

So click that link and what happens? Error page. You need to define the detail.html template. That’s not to hard, just create the file, add the extends base.html instruction at the top and fill in the blank blocks like title and primary. This time there’s no need to loop through anything since there’s only one object. Just call it directly like we did inside the loop on the archive page:
{{object.title}}
etc.

The code might look something like this:

{% block pagetitle %}{{object.title}}{% endblock %}

{% block title %}{{object.title}}{% endblock %}

{% block primary %}

	<ul class="page-nav">

		{% if object.get_previous_published%}

		<li>

			<a href="{{ object.get_previous_published.get_absolute_url }}" title=" {{object.get_previous_published.title}}">« previous</a>



		</li>

		{%endif%}

		{% if object.get_next_published%}

			<li>

				<a href="{{ object.get_next_published.get_absolute_url }}" title=" {{object.get_next_published.title}}">next »</a>

			</li>

		{%endif%}

    </ul>



	<h2>{{ object.title }}</h2>

	<p>{{ object.pub_date }}</p>

	 {{ object.body_html|safe }}

	<p>tags: {% for tag in object.get_tags%}<a href="/blog/tags/{{tag.name|slugify}}/">{{tag}}</a>{% endfor %}</p>



	<p><a href="{{object.get_absolute_url}}">read more</a></p>

{% endblock %}

Note that I’ve made use of those get_next_published and get_previous_published functions that we defined way back when wrote our models. That way, users have some handy next and previous links for navigating through your permalink pages.

Naturally you can get much more sophisticated with your HTML than this simple example.

To create the templates for the tag pages, you’ll essentially do the same thing. In our custom tag view we returned a list of all the entries in an object named object_list. So in order to display them, just loop through object_list like we did with the latest variable above.

More about built-in template filters

Before we move on, a slight digression.

It’s worth paying a visit to the Django documentation on template filters and tags. There’s a whole bunch of useful stuff built in to Django. You can use {% if %} tags to narrow and filter results, and there’s also {% else %}, {% ifequal var1 var2 %}, {% ifnotequal var1 var2 %} and most other common programming structures.

Then there’s a host of template filters, like the truncatewords_html and safe filters we used above. For instance, there’s a handy date filter if you’d like to display your post date in something a little sexier than a UNIX timestamp.

Here’s what that would look like using the “date” filter:

{{object.pub_date|date:"D d M Y"}}

Another great filter is the timesince filter which will automatically convert a date into syntax like “1 week, 2 days ago”.

There are also filters for escaping ampersands, escaping JavaScript, adding line breaks, removing HTML, converting to upper/lowercase and dozens more. In fact in two and a half years of building sites with Django I’ve only needed to write a custom filter once. Naturally your mileage may vary somewhat.

Roll your own template tags

One thing I do frequently do is write custom template “tags.” Template tags are perhaps one of the more confusing aspects of Django, since they still have a little bit of “magic” in them. But luckily they aren’t too hard to work with.

Template tags are a way of extending the Django template system to use it in project specific ways. For instance, custom template tags are a perfect way to handle things that don’t make sense in a view, but do require some database logic.

Perhaps the best example is something like a sidebar. So far ours is empty, but we can easily add a list of our most recent blog posts.

We could write a filter that specifically fetches blog entries, but then what happens when we add links in the next lesson and want to display the most recent links? Write another template filter? That’s not very DRY! Instead, let’s just write a filter that fetches the most recent entries from any model with a date field.

In fact we don’t even need to really write it. James Bennett has already written some great reusable code so we’ll just use that. I strongly recommend that you have read through James’ tutorial so you can see how and why this code works.

Open a new file and paste in this code:

from django.template import Library, Node

from django.db.models import get_model



register = Library()



class LatestContentNode(Node):

    def __init__(self, model, num, varname):

        self.num, self.varname = num, varname

        self.model = get_model(*model.split('.'))



    def render(self, context):

        context[self.varname] = self.model._default_manager.all()[:self.num]

        return ''



def get_latest(parser, token):

    bits = token.contents.split()

    if len(bits) != 5:

        raise TemplateSyntaxError, "get_latest tag takes exactly four arguments"

    if bits[3] != 'as':

        raise TemplateSyntaxError, "third argument to get_latest tag must be 'as'"

    return LatestContentNode(bits[1], bits[2], bits[4])



get_latest = register.tag(get_latest)





Now save that file as “get_latest.py” in a new folder within the blog app named “templatetags”. The folder name and location are important since Django only looks up template tags in specific locations. You also need to create an empty file named “__init__.py” inside the “templatetags” folder.

One thing to note about this code, if you look closely you’ll notice that our template tag is going to fetch all entries, not just the public ones. In other words, our model allows for draft posts, but our template tag doesn’t.

This is the line in question:

self.model._default_manager.all()

There are two ways around this, one is quick and dirty — just change that line to filter only published entries. In other words:

self.model._default_manager.filter(status=1)

The better (and cleaner) way to do it would be overwrite the default manager for our Entry model. However, that’s a little beyond the scope of this article, so for now we’ll just use the quick and dirty method.

Now open up base.html again and add this line at the top:

{% load get_latest %}

That tells Django to load the template tag, but then we need to grab the actual data. So, head down to the sidebar section and replace it with this code:

<div id="secondary">

	<h3>Recent Entries:</h3>

	{% get_latest blog.Entry 5 as recent_posts %}

    <ul class="archive">



    	{% for obj in recent_posts %}

		<li>

			<a href="{{ obj.get_absolute_url }}" title="Permanent Link to {{ obj.title}}">{{ obj.title}}</a>

		</li>

    	{% endfor %}

	</ul>



<div>

If you want to override that in templates that are inheriting from base.html, just wrap that code in a {% block %} tag and then replace it with something else in the new template.

Links and other resources

The Django template system is quite vast in terms of capabilities, and we’ve really just scratched the surface here.

Make sure you read through the documentation for Python programmers as well as the documentation for template authors and familiarize yourself with all the various built in tags and filters. Another great article is Jeff Croft’s Django Templates: An Introduction.

It’s worth noting that there’s an extensive collection of useful filters and template tags on the Django Snippets site. If you ever find yourself needing a custom tag or filter, have a look through there and see if anyone has already written something that works for your project.

I should also point out that if you just don’t for whatever reason like Django’s template system, it’s possible drop in another template language, like Cheetah or others.

Be sure to stop by for our next installment when we’ll tackle a plan to import, store and display our del.icio.us bookmarks. See you then!