Django’s Templates - Python Django Tutorials

Django’s Templates

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

Site templates, at their most basic, are HTML files 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 frontend to create the user experience, with a backend like Django supplying the data to show in the template.

Template Design Philosophy

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

This means a designer can create a complete frontend (HTML, CSS, imagery and user interaction) without having to write a single line of Python or Django code. A designer need only leave HTML comments in the template for a programmer to replace with template tags—plain text markup tags defined by the Django Template Language (DTL).

While the DTL is similar to other template languages that embed template code in plain HTML, the original creators of Django had a specific set of philosophies in mind when 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 to control presentation and presentation-related logic—and that’s it. The template system shouldn’t support functionality that goes beyond this primary goal.

2. Discourage redundancy

Most dynamic web sites use many common site-wide design elements—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 just output HTML. It should be equally good at generating other text-based formats, or 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 templates display nicely in WYSIWYG editors such as Dreamweaver. That is too severe of a limitation and wouldn’t allow the syntax to be as concise as it is.

Django expects template authors to be 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 it as it treats text— 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

  1. assignment to variables; or
  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, essential for making presentation-related decisions.

The Django template system recognizes designers, not programmers, usually create templates. Therefore, the template language should not assume Python knowledge.

8. Safety and security

The template system 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 templating systems myself over the years, I whole-heartedly endorse this approach—the philosophy and design behind the DTL is one of the major advantages 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 last-minute tasks done, Django 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 quickly why Django really is the “framework for perfectionists with deadlines”.

Remember though, template syntax is highly subjective, and programmers’ opinions vary wildly. Python alone has dozens of open source template-language implementations, each created because its developer deemed all existing template languages inadequate.

With 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 along with the DTL to provide developers with options.

Because Django is intended to be a full-stack web framework providing 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 can also be non-HTML files. 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 example 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" %}

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 runtime 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 Variables. E.g., {{ title }}
  • Object Attributes. E.g., {{ page.title }}
  • Dictionary Lookups. E.g., {{ dict.key }}
  • List Indexes. E.g., {{ list_items.0 }}
  • Method Calls. E.g., {{ var.upper }}, {{ mydict.pop }}

With few exceptions, the methods and attributes available to the Python object are also accessible in the template via the dot operator.

Filters modify a variable for display. You apply a filter 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 the most common elements of the DTL in more detail as we work through the book. Chapter 11 includes an exercise covering all common tags and filters, 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 create one 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, you might think 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. This can be a problem when you have two apps in a project with 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. Your 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 in 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 you would do well to follow.

Before moving on, 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 that creates a common look and branding for 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 create it now. When you’re finished, 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 must tell Django where to look by adding a path to the DIRS setting. Let’s 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          # ...

Line 4 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 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 will name this file base.html (new file):

# \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>

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 in the last chapter, these are the same variable names we gave the event title and calendar respectively.

Displaying a Template

Now 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. Modify the index view as follows (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 HttpResponse (line 15) so you can more easily see the changes. You don’t have to remove the HttpResponse import from django.http (line 2) as we’re going to use it in a later chapter.

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 a dictionary of variables and associated data passed back to the browser—and return an HttpResponse.

You can code each of these steps separately in Django, but in the majority of cases, it’s more common (and easier) to use Django’s render() function, which provides a shortcut that executes all three steps in a single function. Using render() is so common that Django added it to the views.py file for you when startapp created the events app (line 1).

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

In our modified views.py (line 16), 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).

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 the HTML codes are escaped!

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

To get Django to render the HTML correctly, you must 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.

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 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 containing content shared by every page on the website and child templates that inherit these shared features from the parent. Child templates can then add content and formatting unique to the child.

This is easier to understand in practice. First, modify your base.html file as follows (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 other templates can replace. 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 placeholder text to render when a child template has not replaced the content block.

If you fire up the development server again and navigate to http://127.0.0.1:8000, you will see that Django now renders your base template (Figure 6-3). Note we didn’t pass a value for page_title to the template, so the page title shows the default.

Figure 6-3: The site base template with no content.

Next, we will 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\events folder and add the following (new file):

# \events\templates\events\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 unique to the child.
  • Line 3. We replace 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 the site template is not tied to the events app.

To display the new child template in the browser, we must modify the index view to load the new template (change 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 < 2000 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 render(request,
9          'events/calendar_base.html',
10         {'title': title, 'cal': cal}
11     )

Only one change: in line 9, I’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. I have also reformatted the render() function to shorten the line length, but the function remains the same otherwise.

Refresh your browser, and the site should look like Figure 6-4. Note the page title and the content have changed.

Figure 6-4: The events calendar is now a child of the site template, and shows the correct title and page content.

Displaying Database Data

So far, we’ve been showing dynamic data generated by Python (dates and a calendar). Displaying formatted database data in a browser is also essential in a modern web application. In the previous example, we created an HTML calendar, added it to the context in the view and used a template to display the formatted calendar at a URL set by a URLconf.

We follow exactly the same process for rendering database data to the browser, except we’re adding a QuerySet to the context, not HTML. Let’s start by adding a simple view to our views.py file (new code):

# myclub_root\events\views.py

# Add this import to the top of your file
from .models import Event

# ...

1  def all_events(request):
2      event_list = Event.objects.all()
3      return render(request,
4          'events/event_list.html',
5          {'event_list': event_list}
6      )

You can see from this view, it takes very little code to render model data to the browser. After importing the Event model from models.py, the view simply retrieves a QuerySet containing all the event records (line 2) and then renders it to a template with the QuerySet added to the context (line 5).

While we’re retrieving all events with the all() function in this example, all the model filtering, sorting and slicing functions we covered in Chapter 4 are available in the view, so you have a great deal of flexibility to decide what records need to be displayed.

For the event records to display in the browser, we still need to add a template and a URLconf. Let’s start with adding a template to our events app (new file):

# \events\templates\events\event_list.html

1  {% extends "base.html" %}
2  
3  {% block title %}Displaying Model Data{% endblock title %}
4  
5  {% block content %}
6    <h1>All Events in Database</h1>
7    <ul>
8      {% for event in event_list %}
9        <li>{{ event.name }}</li>
10     {% endfor %}
11   </ul>
12 {% endblock content %}

This template is not a lot different than the calendar base template, so should be easy to follow. The magic happens in lines 8, 9 and 10. Lines 8 and 10 are a simple {% for %}/{% endfor %} set of template tags for creating a for loop in the template. We’ll cover for loops in templates in more detail in Chapter 11, but for the moment, just remember they behave exactly the same as a Python for loop.

Line 9 displays the name attribute of each event record in the QuerySet as the for loop iterates over the data. In this template, we’re showing the event names in a bulleted list.

Finally, we add a URLconf so we can navigate to the event list in a browser (change in bold):

# myclub_root\events\urls.py

# ...
urlpatterns = [
    path('', views.index, name='index'),
    path('events/', views.all_events, name='show-events'),
    # ...

A simple change—our event list template will show at the URL /events/. Navigate to http://127.0.0.1:8000/events/ in your browser, and the page should look like Figure 6.5.

Figure 6-5: Rendering database data in a browser with a Django view and 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—different than 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 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 will 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    <head>
5      <meta charset="utf-8">
6      <title>
7        {% block title %}
8        {{ page_title|default:"Untitled Page" }}
9        {% endblock title %}
10     </title>
11     <link href="{% static 'main.css' %}" rel="stylesheet" type="text/css">
12   </head>
13   <body>
14     <div id="wrapper">
15     <header id="header">
16       <div id="logo"><img src="{% static 'logo.png' %}" alt="" /></div>
17       <div id="top_menu">Home | Calendar | About | Contact</div>
18       <div id="topbanner"><img src="{% static 'top_banner.jpg' %}" alt="" /></div>
19     </header>
20     <aside id="rightsidebar">
21       <nav id="nav">
22       <ul>
23         <li>Menu 1</li>
24         <li>Menu 2</li>
25         <li>Menu 3</li>
26       </ul>
27       </nav>
28     </aside>
29     <section id="main">
30       {% block content %}
31       <p>Placeholder text in base template. Replace with page content.</p>
32       {% endblock content %}
33     </section>
34     <footer id="footer">Copyright &copy;
35       <script type="text/JavaScript">
36       document.write(new Date().getFullYear());
37       </script> MyClub
38     </footer>
39     </div>
40   </body>
41 </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 11. We’re adding a stylesheet to the template. Using the {% static %} tag avoids hard coding the URL in the template, which is always preferable. The {% static %} tag is replaced with the full path to main.css at runtime.
  • Lines 15 to 19. 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 20 to 28. We’ve added a conventional right sidebar element with a placeholder menu.
  • Lines 34 to 38. Finally, we’ve added a footer element that uses JavaScript to render the current year.

Other than a few tags, there is nothing Django-specific necessary to upgrade the template. This is by design. Django is frontend agnostic, so it will do nothing to stop you adding whatever you like to the frontend—whether that be anything from making the templates responsive with Bootstrap to adding JavaScript frameworks 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 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

You can download these files from the book website (they’re included in the source code), or you can create your own. Either way, put them both in the \myclub_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-6). I am sure you agree that, while it still has a way to go, the new template is much prettier than the last!

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

OK, if you are using the printed textbook, this will look a lot less impressive as the images are black and white, but if you use my code and stylesheets the site will be in color.

Template Includes

Django also provides you with the ability to insert templates into other templates with the include tag. The syntax of the include tag is simple:

{% include <template name> %}

The <template name> can be a string or a variable, for example:

{% include "events/announcements.html" %}

Let’s try the include tag out with a practical example. Announcements are a common function of club websites and often show on several pages on the website. The HTML for displaying announcements is a perfect candidate for an include—we can create a template for the announcements and include that template on each page we want to show the announcements. First, let’s create the announcements template and save the file with our event templates (new file):

# events\templates\events\announcements.html

1  <h2>Announcements</h2>
2  <table>
3    {% for announcement in announcements %}
4    <tr>
5      <td><strong>{{ announcement.date }}</strong></td>
6      <td>{{ announcement.announcement }}</td>
7    </tr>
8    {% empty %}
9    <tr>
10     <td><strong>{% now "m-d-y" %}</strong></td>
11     <td>No Announcements</td>
12   </tr>
13   {% endfor %}
14 </table>

We’re using some new Django template tags here, so let’s have a closer look at some of the important lines of code:

  • Line 3 is the beginning of a Django for loop tag.
  • Lines 5 and 6 execute for each item in the announcements list. The HTML code will render each announcement in a table row, with the date of the announcement in bold.
  • Line 8 is a Django empty tag. The empty tag is an optional, but convenient, way to test if a list doesn’t exist, or is empty. It’s a neater and faster alternative to using an if...else clause to test the list.
  • Lines 9 to 12 only execute if the announcements list is empty or doesn’t exist. They show a message saying there are no announcements. In line 11, we’re using Django’s now tag to show the current date. The now tag takes one parameter—the date format string. For more on date format strings, see the Django documentation.
  • Line 13 is the closing tag for the Django for loop.

To include the new announcements.html template in our event calendar, we need to add one line to the calendar template (change in bold):

# \events\templates\events\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    {% include "events/announcements.html" %}
9  {% endblock content %}

In line 8, I have added an include tag which loads the new template into the calendar template. If you load up the development server now, your calendar page should look like Figure 6-7.

Figure 6-7: The empty announcements list is inserted into the page using Django’s include tag.

To get actual announcements to display, you must pass a list of announcements to the template. In a real website, you would store information on the announcements in a model in your database, but for the sake of showing how the for loop displays announcements in the included template, we will add a hard-coded list to the index view (changes in bold):

# \myclub_root\events\views.py

# ... partial listing

def index(request, year=date.today().year, month=date.today().month):
    # ...
    cal = HTMLCalendar().formatmonth(year, month)
    announcements = [
        {
            'date': '6-10-2020',
            'announcement': "Club Registrations Open"
        },
        {
            'date': '6-15-2020',
            'announcement': "Joe Smith Elected New Club President"
        }
    ]
    return render(request,
        'events/calendar_base.html',
        {'title': title, 'cal': cal, 'announcements': announcements}
    )

Save your views.py file and refresh your browser. Your event calendar should now look like Figure 6.8.

Figure 6-8: The announcements list now displays all the announcements passed to it from the view.

You wouldn’t hard-code announcements in the view like this in a real website, but it is a handy illustration of how you can use a combination of Django’s include tag and for loop to show list data in templates.

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 Django doesn’t 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 11, I have included an exercise that covers all the most common template tags and filters, complete with example implementations.

In the next chapter, we will take a closer look at the Django admin.