Django’s Templates - Python Django Tutorials

Django’s Templates

New to Django? Get up to speed fast with my Django 2 course!  Learn more here.

In Chapter 5, we created a view to show a simple title and calendar in the browser. This is obviously a long way from a fully functioning modern website – we are missing a site template.

Site templates, at their most basic, are HTML files that are displayed by your browser. All websites – from simple, static websites to interactive web applications that work on multiple devices – are built on HTML.

Modern interactive websites are more complex. For example, a modern website will add Cascading Style Sheets (CSS), semantic markup and JavaScript in the front end to create the user experience, with a backend like Django supplying the data to show in the template. However, the fundamentals stay the same.

Design Philosophy

Django’s approach to web design is simple – keep Django logic and code separate from design. It’s very important to understand that Django’s templates are not simply Python code embedded into HTML; it’s not actually possible to execute Python code in a Django template.

This means that it’s possible for a designer to create a complete front end (HTML, CSS, imagery and user interaction) without ever having to write a single line of Python or Django code. A designer need only leave HTML comments in the template that a programmer replaces with template tags – plain text markup tags defined by the Django Template Language (DTL).

While at it’s core the DTL is simply template code embedded in plain HTML, the original creators of Django had a very definite set of philosophies in creating the DTL. These philosophies remain core to Django today. They are:

  1. Separate logic from presentation
  2. Discourage redundancy
  3. Be decoupled from HTML
  4. XML is bad
  5. Assume designer competence
  6. Treat whitespace obviously
  7. Don’t invent a programming language
  8. Ensure safety and security
  9. Extensible

1. Separate logic from presentation

A template system is a tool that controls presentation and presentation-related logic – and that’s it. The template system shouldn’t support functionality that goes beyond this basic goal.

2. Discourage redundancy

The majority of dynamic web sites use some sort of common site-wide design – a common header, footer, navigation bar, etc. The Django template system should make it easy to store those elements in a single place, eliminating duplicate code. This is the philosophy behind template inheritance.

3. Be decoupled from HTML

The template system shouldn’t be designed so that it only outputs HTML. It should be equally good at generating other text-based formats, or just plain text.

4. XML should not be used for template languages

Using an XML engine to parse templates introduces a whole new world of human error in editing templates – and incurs an unacceptable level of overhead in template processing.

5. Assume designer competence

The template system shouldn’t be designed so that templates necessarily are displayed nicely in WYSIWYG editors such as Dreamweaver. That is too severe of a limitation and wouldn’t allow the syntax to be as nice as it is.

Django expects template authors are comfortable editing HTML directly.

6. Treat whitespace obviously

The template system shouldn’t do magic things with whitespace. If a template includes whitespace, the system should treat the whitespace as it treats text – just display it. Any whitespace that’s not in a template tag should be displayed.

7. Don’t invent a programming language

The template system intentionally doesn’t allow the following:

  1. Assignment to variables
  2. Advanced logic

The goal is not to invent a programming language. The goal is to offer just enough programming-esque functionality, such as branching and looping, that is essential for making presentation-related decisions.

The Django template system recognizes that templates are most often written by designers, not programmers, and therefore should not assume Python knowledge.

8. Safety and security

The template system, out of the box, should forbid the inclusion of malicious code – such as commands that delete database records. This is another reason why the template system doesn’t allow arbitrary execution of Python code.

9. Extensibility

The template system should recognize that advanced template authors may want to extend its technology. This is the philosophy behind custom template tags and filters.

DTL Philosophy – Concluding Thoughts

Having worked with many different templating systems myself over the years, I whole-heartedly endorse this approach – the DTL and the way it has been designed is one of the major pluses of the Django framework.

When the pressure is on to Get Stuff Done, and you have both designers and programmers trying to communicate and get all the of the last minute tasks done, Django just gets out of the way and lets each team concentrate on what they are good at.

Once you have found this out for yourself through real-life practice, you will find out very quickly why Django really is the “framework for perfectionists with deadlines”.

Remember though, template syntax is highly subjective, and programmers’ opinions vary wildly. The fact that Python alone has dozens of open source template-language implementations supports this point. Each was likely created because its developer deemed all existing template languages inadequate.

With all this in mind, Django is flexible – it does not require you to use the DTL. All versions of Django since Django 1.8, ship with the popular Jinja2 template engine as well as the DTL to provide developers with options.

Because Django is intended to be a full-stack web framework that provides all the pieces necessary for web developers to be productive, most times it’s more convenient to use the DTL, but it’s not a requirement.

Django Template System Basics

A Django template is a text file. While in the vast majority of cases this text file is an HTML file, Django templates are not restricted to HTML. Non-HTML examples include email templates and CSV templates.

To turn a plain text file into a Django template, the template designer adds template tags, variables and filters.

A template tag is surrounded by {% and &}. A template tag does something. This is deliberately vague because Django’s tags are extremely flexible. Some examples functions performed by template tags are:

  • Display Logic. E.g. {% if %}...{% endif %}
  • Loop Control. E.g. {% for x in y %}...{% endfor %}
  • Block Declaration. E.g. {% block content %}...{% endblock %}
  • Content Import. E.g. {% include "header.html" %}
  • Inheritance. E.g. {% extends "base.html" %}
  • And much more…

It’s also possible to create custom template tags to extend the DTL.

A template variable is surrounded by {{ and }}. A template variable is something. Template variables are passed to the template at run time in the context. We’ll dig deeper into template contexts shortly.

Template variables don’t just handle simple data, they work with more complex data structures too. For example:

  • Simple Variable. E.g. {{ title }}
  • Object Attribute. E.g. {{ page.title }}
  • Dictionary Lookup. E.g. {{ dict.key }}
  • List Index. E.g. {{ list_items.0 }}
  • Method Call. E.g. {{ var.upper }}

Filters modify a variable for display. A filter is applied to a variable using the pipe (|) character. There are dozens of built-in filters, here are some examples:

  • Change Case. E.g. {{ name|title }} or {{ units|lower }}
  • Truncation. E.g. {{ post_content|truncatewords:50 }}
  • Date Formatting. E.g. {{ order_date|date:"D M Y" }}
  • List Slicing. E.g. {{ list_items|slice:":3" }}
  • Default Values. E.g. {{ item_total|default:"nil" }}

This is a sample of the template tags, variable methods and filters available in Django. We’ll be covering all of the most common elements of the DTL in more detail as we work through the book. Chapter [TODO] includes an exercise that covers all of the common tags and filter, complete with example implementations of each.

How Django Finds Templates

When startproject created your Django site, it added a TEMPLATES setting to your settings.py file that looks like this:

1  TEMPLATES = [
2      {
3          'BACKEND': 'django.template.backends.django.DjangoTemplates',
4          'DIRS': [],
5          'APP_DIRS': True,
6          'OPTIONS': {
7              'context_processors': [
8                  'django.template.context_processors.debug',
9                  'django.template.context_processors.request',
10                 'django.contrib.auth.context_processors.auth',
11                 'django.contrib.messages.context_processors.messages',
12             ],
13         },
14     },
15 ]

The most important line to note here is Line 5. When APP_DIRS is True, the Django template engine will look for templates in a folder called “templates” in each app listed in INSTALLED_APPS.

Django doesn’t create the folder for you, so let’s go ahead and do that now for our events app. Once you have added the folder, your directory tree should look like this:

\events
    \migrations
    \templates
    __init.py__
    # ...

Following on from this, adding templates to your project should be as simple as adding template files to your templates folder. In a single app project you can get away with this, but it’s not recommended.

Why? Because Django uses short-circuit logic when searching for templates which is a problem when you have two apps in a project that have a template with the same name.

Say you have created an index.html template for your events app and you add a 3rd party app that also uses a template called index.html. You folder structure will look like this:

\events
    \templates
        index.html
\other_app
    \templates
        index.html

When you tell Django to load the template index.html it will load the first file it finds (based on the order of your apps in the INSTALLED_APPS setting). So, if you want to load the index template for other_app, but the events app is listed first in INSTALLED_APPS, Django will load the wrong template.

We solve this problem by namespacing our templates. Namespacing templates is simple – we add a folder named after the app to our templates folder. This is what the above example looks like after namespacing the templates:

\events
    \templates
        \events
            index.html
\other_app
    \templates
        \other_app
            index.html

Now, when you want to load a template, you include the namespace (“events/index.html” or “other_app/index.html” in this example) and Django will always load the correct template.

As with most things Django, namespacing templates is a convention, not a hard and fast rule. But, if you want maximum portability for your apps and to avoid some headaches later on, it is a convention that would do well to follow.

Before moving on, go ahead and add the new folder to your events app. When you’re done, the folder structure will look like this:

\events
    \migrations
    \templates
        \events
    __init.py__
    # ...

Creating a Site Template

All modern websites have a site template; a common look or branding that is duplicated across every page on the website.

The most common place for storing site template files in Django is in the website app that Django created automatically for you when you ran startproject. Django didn’t create the templates folder for you, so go ahead and do that now. When you are done, your folder structure should look like this:

\myclub_site
    \templates
    __init__.py
    ...

As your website app is not in INSTALLED_APPS, Django won’t automatically look for templates in the \myclub_site\templates folder, you have to tell Django where to look by adding a path to the DIRS setting (Line 4 on page [TODO]). Let’s go ahead and modify settings.py (changes in bold):

1  TEMPLATES = [
2      {
3          'BACKEND': 'django.template.backends.django.DjangoTemplates',
4          'DIRS': [os.path.join(BASE_DIR, 'myclub_site/templates')],
5          'APP_DIRS': True,
6          # ...

This looks complicated, but is easy to understand – os.path.join is a Python command to create a file path by joining strings together (concatenating). In this example, we are joining myclub_site/templates to our project directory to create the full path to our templates directory, i.e., <your project path>/myclub_root/myclub_site/templates.

The DIRS list is not just for letting Django know where your site templates are – it’s useful for listing any template resources that exist outside of your existing apps. Note that Django will search your DIRS list in the order listed, so keep in mind my previous warnings about templates with the same name when linking to external resources.

Now that we have the template folder created and the folder path listed so Django can find our site template, it’s time to create a simple template. We’re going to name this file base.html:

# \myclub_site\templates\base.html

1  <!doctype html>
2  <html>
3      <head>
4          <meta charset="utf-8">
5          <title>Basic Site Template</title>
6      </head>
7  
8      <body>
9          <h1>{{ title }}</h1>
10         <p>{{ cal }}</p>
11     </body>
12 </html>

You will not that this is plain HTML except for lines 9 and 10. In Line 9, we’ve created a Django variable tag and named it title and in Line 10, we’ve created another variable tag and named it cal. If you remember from the view we created last chapter, these are the same variable names we gave the event title and calendar respectively.

Displaying a Template

Now that we’ve created the template, we need to tell Django to use our new base template when displaying content on the site. This is done in your views.py file. Make the following changes to the index view (changes in bold):

# \events\views.py

1  from django.shortcuts import render
2  # from django.http import HttpResponse
3  from datetime import date
4  import calendar
5  from calendar import HTMLCalendar
6  
7  
8  def index(request, year=date.today().year, month=date.today().month):
9      year = int(year)
10     month = int(month)
11     if year < 1900 or year > 2099: year = date.today().year
12     month_name = calendar.month_name[month]
13     title = "MyClub Event Calendar - %s %s" % (month_name, year)
14     cal = HTMLCalendar().formatmonth(year, month)
15     # return HttpResponse("<h1>%s</h1><p>%s</p>" % (title, cal))
16     return render(request, 'base.html', {'title': title, 'cal': cal})

For our new view, we have replaced the call to HttpResponse() with a call to render(). I have commented out the original line (Lines 2 and 15) so that you can more easily see the changes. You don’t have to remove the import from django.http, but it’s good practice not to import modules that you are no longer using.

render() is a special Django helper function that creates a shortcut for communicating with a web browser. If you remember from Chapter 5, when Django receives a request from a browser, it finds the right view and the view returns a response to the browser.

In the example from Chapter 5, we simply returned some HTML text. However, when we wish to use a template, Django first must load the template, create a context – which is basically a dictionary of variables and associated data that is passed back to the browser – and then return a HttpResponse.

You can code each of these steps separately in Django, but in the vast majority of cases it’s more common (and easier) to use Django’s render() function, which provides a shortcut that provides all three steps in a single function.

When you supply the original request, the template and a context directly to render(), it returns the appropriately formatted response without you having to code the intermediate steps.

In our modified views.py, we are returning the original request object from the browser, the name of our site template and a dictionary (the context) containing our title and cal variables from the view.

Once you have modified your views.py file, save it and fire up the development server. If you navigate to http://127.0.0.1:8000/, you should see your simple new site template (Figure 6-1).

The Unformatted Base Template

Figure 6-1: The unformatted base template

Hmm. Something isn’t quite right – the calendar is rendering as plain text, not as HTML. If you look at the page source, you can see why:

~~ snip

<p>&lt;table border=&quot;0&quot; cellpadding=&quot;0&quot; 
cellspacing=&quot;0&quot; class=&quot;month&quot;&gt;&lt;tr&gt;
&lt;th colspan=&quot;7&quot; class=&quot;month&quot;
&gt;May 2019&lt;/th&gt;&lt;/tr&gt; ...

All of the HTML codes have been escaped!

This is because, by default, Django autoescapes all code before sending it to the browser. This is a built-in security feature that is designed to automatically protect browsers from a hacker inserting malicious code into your site code.

To get Django to render the HTML correctly, you have to turn autoescape off for the calendar code. As this is a common task, the Django developers created the autoescape tag to make life easy for you. Make the following changes to your base.html file (changes in bold):

# \myclub_site\templates\base.html

1  <!doctype html>
2  <html>
3      <head>
4          <meta charset="utf-8">
5          <title>Basic Site Template</title>
6      </head>
7  
8      <body>
9          <h1>{{ title }}</h1>
10         <p>{% autoescape off %}{{ cal }}{% endautoescape %}</p>
11     </body>
12 </html>

Now, when you refresh your browser, the site homepage should look like Figure 6-2.

Your site template rendered correctly

Figure 6-2: The site template rendered with autoescape off for the calendar code.

Template Inheritance

While our base template is rendering fine in the browser, it’s not an effective site template because it’s tied to the events app. A true site template needs to be independent of all the other apps in your Django project.

This is where Django’s template inheritance comes in handy.

With Django’s template inheritance, you create a parent template that contains the common content shared by every page on the website and child templates that inherit these common features from the parent and add additional content and formatting unique to the child.

This is easier to understand in practice. First, make the following changes to your base.html file (changes in bold):

# \myclub_site\templates\base.html

1  <!doctype html>
2  <html>
3      <head>
4          <meta charset="utf-8">
5          <title>
6              {% block title %}
7              {{ page_title|default:"Untitled Page" }}
8              {% endblock title %}
9          </title>
10     </head>
11     
12     <body>
13         {% block content %}
14         <p>Placeholder text in base template. Replace with page content.</p>
15         {% endblock content %}
16     </body>
17 </html>

Let’s have a closer look at what’s changed:

  • Lines 6 and 8. I have added a pair of Django block tags. The block tag defines a block of text that can be replaced by other templates. I’ve named the block title.
  • Line 7. I’ve created a new template variable called page_title. I have also added a default filter to the variable. If a view passes a value for page_title in the context, the template will render the page_title variable in the page title, otherwise it will render “Untitled Page”.
  • Lines 13 and 15. Another pair of Django block tags to define a replaceable block for the page content.
  • Line 14 is simply placeholder text to render when the content block has not been replaced by a child template.

If you fire up the development server again and navigate to http://127.0.0.1:8000, you will see that Django is now rendering your base template (Figure 6-3). Note that since a value for page_title was not passed to the template, the page title has been set to the default.

The site base template

Figure 6-3: The site base template

Next, we’re going to create a child template for the events calendar that inherits common content from the base template and then adds new content unique to the event calendar. Create a new file called calendar_base.html in your events\templates folder and add the following:

# \events\templates\calendar_base.html

1  {% extends 'base.html' %}
2  
3  {% block title %}{{ title }}{% endblock title %}
4  
5  {% block content %}
6      <h1>{{ title }}</h1>
7      <p>{% autoescape off %}{{ cal }}{% endautoescape %}</p>
8  {% endblock content %}

Let’s have a look at this file to see what’s going on:

  • Line 1. The {% extends %} template tag is where Django’s template inheritance magic happens. When you add the {% extends %} tag to a template file, you are telling Django to load all the content from the parent template (base.html). All you have to do in the child is define what blocks the child replaces and add any additional HTML and code that is unique to the child.
  • Line 3. We’re replacing the title block tag from base.html with a new block that will contain the title variable from the index view.
  • Lines 5 and 8. We’re replacing the content block from base.html with a new content block.
  • Lines 6 and 7. These are the same lines that were originally in the base template – they’ve been moved to calendar_base.html so that the site template is no longer tied to the events app.

To be able to display the new child template in the browser, we must modify the index view to load the new template (changes in bold):

# \events\views.py

# ...

1  def index(request, year=date.today().year, month=date.today().month):
2      year = int(year)
3      month = int(month)
4      if year < 1900 or year > 2099: year = date.today().year
5      month_name = calendar.month_name[month]
6      title = "MyClub Event Calendar - %s %s" % (month_name, year)
7      cal = HTMLCalendar().formatmonth(year, month)
8      # return HttpResponse("<h1>%s</h1><p>%s</p>" % (title, cal))
9      return render(request, 'events/calendar_base.html', {'title': title, 'cal': cal})

A single change: in Line 9, we’ve replaced the base.html template with the calendar_base.html template. Note the namespacing on the template to ensure Django always selects the right template.

Refresh your browser and the site should look like Figure 6-4. Note the page title has changed as well as the content.

The events calendar is now a child of the site template

Figure 6-4: The events calendar is now a child of the site template.

Loading Static Files

The basic site template and event calendar are functioning OK, but they’re not very pretty – the site lacks a stylesheet, images and other niceties that make up a professional website.

Django treats static files – images, CSS and JavaScript – differently to templates. Django’s creators wanted it to be fast and scalable, so right from the beginning Django was designed to make it easy to serve static media from a different server to the one the main Django application was running on.

Django achieves speed and scalability by keeping static media in a different directory to the rest of the application. This directory is defined in the settings.py file and is called static by default:

STATIC_URL = '/static/'

This line should be at or near the end of your settings.py file. We need to add another setting so that Django can find the static files for our site. Add the following below the STATIC_URL setting:

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'myclub_site/static'),
]

The STATICFILES_DIRS list serves the same function for static files as the DIRS list does for templates. In this case, we are telling Django to look for static files in the static directory in our site root. Now we need to create a static folder in our site root. Once you have created the new folder, your project directory will look like this:

\myclub_root
    \myclub_site
        \static
        \templates
    # more files ...

Now we’ve created a folder for our static media, we’re going to modify the base.html template to include static media, create a stylesheet and add a logo and a top banner image to the site. We’ll be working with four files:

  1. base.html. We’ll update this file to include media and additional structural elements.
  2. main.css. A new stylesheet for the site.
  3. logo.png. Create or upload a logo for the site.
  4. top_banner.jpg. Create or upload an 800x200px banner image for the site.

Listing 1: base.html

Let’s start with the modified base template (changes in bold):

# \myclub_site\templates\base.html

1  {% load static %}
2  <!doctype html>
3  <html>
4  
5  <head>
6      <meta charset="utf-8">
7      <title>
8          {% block title %}
9          {{ page_title|default:"Untitled Page" }}
10         {% endblock title %}
11     </title>
12     <link href="{% static 'main.css' %}" rel="stylesheet" type="text/css">
13 </head>
14 
15 <body>
16     <div id="wrapper">
17         <header id="header">
18             <div id="logo"><img src="{% static 'logo.png' %}" alt="" /></div>
19             <div id="top_menu">Home | Calendar | About | Contact</div>
20             <div id="topbanner"><img src="{% static 'top_banner.jpg' %}" alt="" /></div>
21         </header>
22         <aside id="rightsidebar">
23             <nav id="nav">
24                 <ul>
25                     <li>Menu 1</li>
26                     <li>Menu 2</li>
27                     <li>Menu 3</li>
28                 </ul>
29             </nav>
30         </aside>
31         <section id="main">
32             {% block content %}
33             <p>Placeholder text in base template. Replace with page content.</p>
34             {% endblock content %}
35         </section>
36         <footer id="footer">Copyright &copy;
37             <script type="text/JavaScript">
38                 document.write(new Date().getFullYear());                    
39             </script> MyClub
40         </footer>
41      </div>
42 </body>
43 
44 </html>

Most of the new code in this file is plain HTML5 markup, however there are few new elements worth noting:

  • Line 1. The {% load static %} tag links the static elements in the template to your STATIC_ROOT.
  • Line 12. We’re adding a stylesheet to the template. Using the {% static %} tag avoids hard coding the URL in the template which is always preferable from a portability perspective. The {% static %} tag will be replaced with the full path to main.css at runtime.
  • Lines 17 to 21. We’ve added a <header> section to the template and added a logo, placeholder for the top menu and a banner image. Note the use of the {% static %} tag again when loading resources.
  • Lines 22 to 30. We’ve added a conventional right sidebar element with a placeholder menu.
  • Lines 36 to 40. Finally, we’ve added a footer element that uses JavaScript to render the current year.

Notice how, other than a few tags, there is nothing Django-specific you had to do to upgrade the template. This is by design. Django is designed to be front-end agnostic, so it will do nothing to get in the way of you adding whatever you like to the front-end – whether that be anything from making the templates responsive with Bootstrap to adding JavaScript framworks like jQuery and Angular.

Listing 2: main.css

# \myclub_site\static\main.css

1   @charset "utf-8";
2   #header {
3       border-style: none;
4       width: 800px;
5       height: auto;
6   }
7   #wrapper {
8       margin-top: 0px;
9       margin-left: auto;
10      margin-right: auto;
11      background-color: #FFFFFF;
12      width: 800px;	
13  }
14  body {
15      background-color: #E0E0E0;
16      font-family: "Trebuchet MS", Helvetica, sans-serif;
17      font-size: 0.9em;
18      text-align: justify;
19      color: #474747;
20  }
21  h1 {
22      color: #270c39;
23  }
24  #footer {
25      text-align: center;
26      font-size: 0.8em;
27      padding-top: 10px;
28      padding-bottom: 10px;
29      background-color: #FFFFFF;
30      border-top: thin solid #BBBBBB;
31      clear: both;
32      color: #969696;
33  }
34  #nav li {
35      padding-top: 10px;
36      padding-bottom: 10px;
37      font-size: 1em;
38      list-style-type: none;
39      border-bottom: thin solid #e0e0e0;
40      color: #1e9d36;
41      left: 0px;
42      list-style-position: inside;
43  }
44  #nav li a {
45      text-decoration: none;
46  }
47  #rightsidebar {
48      width: 180px;
49      height: 350px;
50      float: right;
51      padding-right: 20px;
52  }
53  #main {
54      width: 560px;
55      float: left;
56      margin: 0 10px 50px 20px;	
57      padding-right: 10px;
58  }
59  #logo {
60      padding: 10px;
61      float: left;
62  }
63  #top_menu {
64      float: right;
65      padding: 50px 20px 0px 0px;
66      font-size: 1.2em;
67      color: #270c39;
68  }
69  table.month td {
70      border: 1px solid rgb(221, 221, 221);
71      padding: 20px;
72      text-align: center;
73  }
74  table.month th {
75      padding: 2px 0px;
76      text-align: center;
77  }
78  th.month {
79      display: none;
80  }

This file is standard CSS. If you are not familiar with CSS, you can either enter the code as written and learn more about style sheets as you go, or if you want to learn more now, you can check out W3schools.

logo.png and top_banner.jpg

These files can either be downloaded from the book website, or you can create your own. Either way, they both need to be put into the \mfdw_site\static\ folder.

If you fire up the development server again and navigate to http://127.0.0.1:8000, you will see the results of your efforts (Figure 6-5). I am sure you will agree that, while it still has a way to go, the new template is much prettier than the last!

The event calendar template with styling and structural elements added

Figure 6-5: The event calendar template with styling and structural elements added.

Chapter Summary

In this chapter we covered the basics of how Django’s templates work, how to pass information from a Django view to the browser and how little Django does to get in the way of you creating a professional, modern look for your website.

I also introduced you to some of the more common Django template tags and filters. In Chapter [TODO] I have included an exercise that covers all of the most common template tags and filters, complete with example implementations.

In the next chapter, we’re going to take a closer look at the Django admin.