9. Working with Templates

So far we’ve created several Django HTML templates for different pages in the application. You’ve probably already noticed that there is a lot of repeated HTML code in these templates.

While most sites will have lots of repeated structure (i.e. headers, sidebars, footers, etc) repeating the HTML in each template is a not good way to handle this. So instead of doing the same cut and paste hack job, we can minimize the amount of repetition in our code base by employing template inheritance provided by Django’s Template Language.

The basic approach to using inheritance in templates is as follows.

  1. Identify the re-occurring parts of each page that are repeated across your application (i.e. header bar, sidebar, footer, content pane)
  2. In a base template, provide the skeleton structure of a standard page along with any common content (i.e. the copyright notice that goes in the footer, the logo and title that appears in the section), and then define a number of blocks which are subject to change depending on which page the user is viewing.
  3. Create specific templates - all of which inherit from the base template - and specify the contents of each block.

9.1. Reoccurring HTML and The Base Template

Given the templates that we have created so far it should be pretty obvious that we have been repeating a fair bit of HTML code. Below we have abstracted away any page specific details to show the skeleton structure that we have been repeating within each template.

<!DOCTYPE html>

<html>
    <head>
        <title>Rango</title>
    </head>

    <body>
        <!-- Page specific content goes here -->
    </body>
</html>

Let’s make this our base template, for the time being, and save it as base.html in Rango’s templates directory (e.g. templates/rango/base.html).

Note

You should always aim to extract as much reoccurring content for your base templates. While it may be a bit more of a challenge for you to do initially, the time you will save in maintenance of your templates in the future far outweighs the initial overhead. Think about it: would you rather maintain one copy of your markup or multiple copies?

Warning

Remember that your page <!DOCTYPE html> declaration absolutely must be placed on the first line for your page! Not doing so will mean your markup will not comply with the W3C HTML5 guidelines.

9.2. Template Blocks

Now that we’ve identified our base template, we can prepare it for our inheriting templates. To do this, we need to include a Template Tag to indicate what can be overridden in the base template - this is done through the use of blocks.

Add a body_block to the base template as follows:

<!DOCTYPE html>

<html>
    <head>
        <title>Rango</title>
    </head>

    <body>
        {% block body_block %}{% endblock %}
    </body>
</html>

Recall that standard Django template commands are denoted by {% and %} tags. To start a block, the command is block <NAME>, where <NAME> is the name of the block you wish to create. You must also ensure that you close the block with the endblock command, again enclosed within Django template tags.

You can also specify ‘default content’ for your blocks, if you so desire. Our body_block defined above presently has no default content associated with it. This means that if no inheriting template were to employ the use of body_block, nothing would be rendered - as shown in the code snippet below.

<!DOCTYPE html>

<html>
    <head>
        <title>Rango</title>
    </head>

    <body>

    </body>
</html>

However, we can overcome this by placing default content within the block definition, like so:

<!DOCTYPE html>

<html>
    <head>
        <title>Rango</title>
    </head>

    <body>
        {% block body_block %}This is body_block's default content.{% endblock %}
    </body>
</html>

If a template were to inherit from the base template without employing the use of body_block, the rendered outcome would now look something like the markup shown below.

<!DOCTYPE html>

<html>
    <head>
        <title>Rango</title>
    </head>

    <body>
        This is body_block's default content.
    </body>
</html>

Hopefully this all makes sense - and for now, we’ll be leaving body_block blank by default. All of our inheriting templates will be making use of body_block. You can place as many blocks in your templates as you so desire. For example, you could create a block for the page title, meaning you can alter the title of each page while still inheriting from the same base template.

Blocks are a really powerful feature of Django’s template system to learn more about them check out the official Django documentation on templates.

9.2.1. Abstracting Further

Now that you have an understanding of Django blocks, let’s take the opportunity to abstract our base template a little bit further. Reopen the base.html template and modify it to look like the following.

<!DOCTYPE html>

<html>
    <head>
        <title>Rango - {% block title %}How to Tango with Django!{% endblock %}</title>
    </head>

    <body>
        <div>
            {% block body_block %}{% endblock %}
        </div>

        <hr />

        <div>
            <ul>
            {% if user.is_authenticated %}
                <li><a href="/rango/restricted/">Restricted Page</a></li>
                <li><a href="/rango/logout/">Logout</a></li>
                <li><a href="/rango/add_category/">Add a New Category</a></li>
            {% else %}
                <li><a href="/rango/register/">Register Here</a></li>
                <li><a href="/rango/login/">Login</a></li>
            {% endif %}

                <li><a href="/rango/about/">About</a></li>
            </ul>
        </div>
    </body>
</html>

We introduce two new features into the template.

  • The first is a new Django template block, title. This will allow us to specify a custom page title for each page inheriting from our base template. If an inheriting page does not make use of this feature, the title is defaulted to Rango - How to Tango with Django!
  • We also bring across the list of links from our current index.html template and place them into a HTML <div> tag underneath our body_block block. This will ensure the links are present across all pages inheriting from the base template. The links are preceded by a horizontal rule (<hr />) which provides a visual separation between the body_block content and the links.

Also note that we enclose the body_block within a HTML <div> tag - we’ll be explaining the meaning of the <div> tag in Chapter 19. Our links are also converted to an unordered HTML list through use of the <ul> and <li> tags.

9.3. Template Inheritance

Now that we’ve created a base template with a block, we can now update the templates we have created to inherit from the base template. For example, let’s refactor the template rango/category.html.

To do this, first remove all the repeated HTML code leaving only the HTML and Template Tags/Commands specific to the page. Then at the beginning of the template add the following line of code:

{% extends 'rango/base.html' %}

The extends command takes one parameter, the template which is to be extended/inherited from (i.e. rango/base.html). We can then modify the category.html template so it looks like the following complete example.

Note

The parameter you supply to the extends command should be relative from your project’s templates directory. For example, all templates we use for Rango should extend from rango/base.html, not base.html.

{% extends 'rango/base.html' %}

{% block title %}{{ category_name }}{% endblock %}

{% block body_block %}
    <h1>{{ category_name }}</h1>

    {% if pages %}
    <ul>
        {% for page in pages %}
        <li><a href="{{ page.url }}">{{ page.title }}</a></li>
        {% endfor %}
    </ul>
    {% else %}
        <strong>No pages currently in category.</strong>
    {% endif %}

    {% if user.is_authenticated %}
       <a href="/rango/category/{{category_name_url}}/add_page/">Add a Page</a>
    {% endif %}
{% endblock %}

Now that we inherit from base.html, all that exists within the category.html template is the extends command, the title block and the body_block block. You don’t need a well-formatted HTML document because base.html provides all the groundwork for you. All you’re doing is plugging in additional content to the base template to create the complete HTML document which is sent to the client’s browser.

Note

Templates are very powerful and you can even create your own template tags. Here we have shown how we can minimise the repetition of structure HTML in our templates.

However, templates can also be used to minimise code within your application’s views. For example, if you wanted to include the same database-driven content on each page of your application, you could construct a template that calls a specific view to handle the repeating portion of your webpages. This then saves you from having to call the Django ORM functions which gather the required data for the template in every view that renders it.

To learn more about the extensive functionality offered by Django’s template language, check out the official Django documentation on templates.

9.4. Exercises

Now that you’ve worked through this chapter, we’ve got several exercises for you to work through. After completing them, you’ll be a Django templating pro.

  • Update all other existing templates within Rango’s repertoire to extend from the rango/base.html template. Follow the same process as we demonstrated above. Once completed, your templates should all inherit from base.html, as demonstrated in Figure 1. While you’re at it, make sure you remove the links from our index.html template. We don’t need them anymore! You can also remove the link to Rango’s homepage within the about.html template.
  • Convert the restricted page to use a template. Call the template restricted.html, and ensure that it too extends from our base.html template.
  • Add another link to our growing link collection that allows users to navigate back to Rango’s homepage from anywhere on the website.

Warning

Remember to add {% load static %} to the top of each template that makes use of static media. If you don’t, you’ll get an error! Django template modules must be imported individually for each template that requires them - you can’t make use of modules included in templates you extend from!

Figure 1: A class diagram demonstrating how your templates should inherit from base.html.

Note

Upon completion of these exercises, all of Rango’s templates should inherit from base.html. Looking back at the contents of base.html, the user object - found within the context of a given Django request - is used to determine if the current user of Rango is logged in (through use of user.is_authenticated). As all of Rango’s templates should inherit from this base template, we can say that all of Rango’s templates now depend on having access to the context of a given request.

Due to this new dependency, you must check each of Rango’s Django views. For each view, ensure that the context for each request is made available to the Django template engine. Throughout this tutorial, we’ve been using render_to_response() to achieve this. If you don’t ensure this happens, your views may be rendered incorrectly - users may appear to be not logged in, even though Django thinks that they are!

As a quick example of the checks you must carry out, have a look at the about view. Initially, this was implemented with a hard-coded string response, as shown below. Note that we only send the string - we don’t make use of the request passed as the request parameter.

def about(request):
    return HttpResponse('Rango says: Here is the about page. <a href="/rango/">Index</a>')

To employ the use of a template, we call the render_to_response() function and use the RequestContext class to obtain the request’s current context. This will allow the template engine access to objects such as user, which will now allowing the template engine to determine if the user is logged in.

def about(request):
    context = RequestContext(request)
    return render_to_response('rango/about.html', {}, context)

Remember, the second parameter of render_to_response() is a dictionary with which you can use to pass additional data to the Django template engine. Have a look at Section 4.1.3 to refresh your memory on render_to_response().