• 12 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 7/20/22

Separate App Logic From Presentation With a Django Template

Understand Templates

We’re going to start this chapter by demonstrating an anti-pattern.

In programming, a pattern is a style or technique that represents best practice and leads to good outcomes. Conversely, an anti-pattern represents bad practice, something you should avoid. 

To demonstrate, let’s fill out one of our pages with some more HTML.

Open up listings/views.py, and find your  hello  view. Begin adding structural elements of a web page like  <head> ,  <body> ,  <title> , and the all-enclosing <html> tag. 

You could achieve that by adding more HTML to your view, like this:

def hello(request):
    bands = Band.objects.all()
    return HttpResponse(f”””
        <html>
            <head><title>Merchex</title><head>
            <body>
                <h1>Hello Django!</h1>
                <p>My favourite bands are:<p>
                <ul>
                    <li>{bands[0].name}</li>
                    <li>{bands[1].name}</li>
                    <li>{bands[2].name}</li>
                </ul>
            </body>
        </html>
    ”””)

This code works, but it’s an anti-pattern.

Why, what’s wrong with this approach?

Well, our view is already starting to look a little busy, and the amount of HTML here will only get larger as we continue to build the app.

The problem is that the view now has two responsibilities:

  • Selecting all  Band  objects from the database - the logic.

  • Displaying the names of those bands among other content like headings and paragraphs - the presentation

If one part of the application has too many responsibilities, it becomes unmanageable.

How can we turn this from an anti-pattern into a pattern and use best practices? 

We’re going to follow the single-responsibility principle by moving the responsibility for presentation out of the view and placing it in its rightful home: a template.

Add an HTML Template File to the Templates Directory

Start by creating a new file at:

listings/templates/listings/hello.html

Let’s fill out this file with HTML:

# listings/templates/listings/hello.html

<html>
    <head><title>Merchex</title></head>
    <body>
        <h1>Hello Django!</h1>
        <p>My favourite bands are:</p>
        <!-- TODO: list bands -->
    </body>
</html>

Doesn’t HTML code look so much better in an .html file than in a .py file? You get proper syntax highlighting, and auto-indentation!

Update View to Render that Template File

Now let’s update the view so that it renders our template rather than defining its own HTML:

# listings/views.py

...
from django.shortcuts import render
...

def hello(request):
    bands = Band.objects.all()
    return render(request, 'listings/hello.html')

First, import the  render  function. It is probably already there since it’s included in the boilerplate code, but add it if you need to. 

In the return statement, you’re no longer calling the HttpResponse  constructor. Instead, call the  render  function with two arguments:

  • The  request  object that is passed into the  hello  function.

  • A string with the path to the template file we created. 

Let’s take a look at the page in the browser:

The web page displays Hello Django! My favourite bands are:
The Hello page.

This is a great start, but the band names are no longer appearing on the page.

We need a way to inject the model data into our template.

Pass a Context Object to the Template Containing a List of Objects

Let’s go back to our view and add a third argument to our call to the  render  method. This argument must be a Python  dict . 

# listings/views.py

...
return render(request,
            'bands/hello.html',
            {'first_band': bands[0]})

 This dictionary is called a context dictionary. Each key becomes a variable you can use in your template, like this: 

# listings/templates/listings/hello.html

...
<p>My favourite bands are:</p>
    <ul>
        <li>{{ first_band.name }}</li>
    </ul>
...

Let’s look at the page in our browser to see that in action:

The web page now lists De La Soul, prefaced by a bullet, as a favourite band.
The first band added.

What are these curly brackets? That’s not HTML! 

Well spotted! Django templates can include this curly bracket syntax within valid HTML, also known as the Django template language. Wherever you see double curly brackets containing a variable name, the value of that variable will be inserted. These are called template variables

We could continue adding each band to the context dictionary individually, but let’s save time and pass them all at once:

# merchex/listings/views.py

    ...
    return render(request,
                'bands/hello.html',
                {'bands': bands})

And then in our template:

# listings/templates/listings/hello.html

...
<p>My favourite bands are:</p>
    <ul>
        <li>{{ bands.0.name }}</li>
        <li>{{ bands.1.name }}</li>
        <li>{{ bands.2.name }}</li>
    </ul>
...
The web page now displays three favourite bands in a bullet list: De La Soul, Cut Copy, and Foo Fighters.
All three bands.

In summary, templates are a way to define the content on a page that doesn’t change. Inside these templates, insert template variables, which act as placeholders for the content that does change. Let's check this out! 

When you render a template in a view, pass a context dictionary to the template, and the context variables are injected into their respective placeholders.

By keeping the view free from any presentational code (HTML), you can limit the view’s responsibility to one thing only - the logic for retrieving the correct data from the database and injecting it into the page.

Now that you understand the basics of templates let’s look at some more useful template functionality.

Iterate Over a List in a Template

So far, our example code has assumed that we will always have a fixed number of bands (three). But what if you deleted one in the database? A reference to  bands[2]  would now throw an error, as there would be no item at index  2  of the list. On the other hand, if we have more than three bands, we might also want to display those additional bands on the page. 

In programming, when you have to deal with a list of an unknown length, use a  for  loop, which iterates over each item and then stops when there are no more left. The Django template language has its own syntax for looping.

In our template, let’s replace the fixed length list with a loop:

# listings/templates/listings/hello.html

<p>
    My favourite bands are:
    {% for band in bands %}
        {{ band.name }},
    {% endfor %}
</p>

Here are some things to note:

  • Looping and other logic statements are enclosed in brackets and percent-signs ( {% … %} ). These are called template tags.

  • A  for  statement is constructed using similar syntax as Python, but without the trailing colon ( : ).

  • The tendency is to use the conventional Python  for singular in plural  format in  for  statements.

  • A  for  template tag must have a closing  endfor  tag later on in the template.

  • The space between the  for  and  endfor  tags can contain text,  HTML, and even Django template variables. 

Wait… you said that logic belongs in the view, and presentation belongs in the template and that we should keep the two separate. But we just added a  for  loop to our template. Isn’t that logic?

Ok, ok, you’re right! The lines are a little blurred. Django’s design philosophy here is that “a template system [is] a tool that controls presentation and presentation-related logic” (Source: Django Documentation). So Django does allow some logic in its templates. But the logic for, say, making calls to the database is not related to presentation and belongs firmly in the view. 

Let’s take a look at our page in the browser:

The web page now displays the three bands in an inline list instead of as a vertical bullet list.
No more bullet list.

We’ve got a slightly annoying trailing comma there, but rather than spending time fixing that, let’s update the loop to render our bands as an HTML unordered list, like we had before:

# listings/templates/listings/hello.html

<p>My favourite bands are:</p>
<ul>
    {% for band in bands %}
        <li>{{ band.name }}</li>
    {% endfor %}
</ul>

 Notice how the  <ul>  opening and closing tags are outside the loop - because we only want those to appear once. The<li>  tags are inside the loop, because we need those to repeat - once for each band on the list. 

We’re back to our list, but now we’re rendering it with a loop:

The web page now displays the three bands in a bullet list.
Rendered with a loop.

Use a Template Filter (e.g. truncate) in a Template

Sometimes when you inject a variable into your template, you might want to apply some formatting. Template filters can help you here. For example, say you wanted to display the band names in a different case. You could use the  lower  or  upper filter, like this: 

# example code

<li>{{ band.name|upper }}</li>
The web page now displays the bands in upper case.
Upper case.

You apply a filter to a variable by using the pipe ( | ) character. 

Filters can do more than just text formatting. Let’s see how you would print out the number of bands in our list by applying the  length  filter to the list of bands:

# example code

<p>I have {{ bands|length }} favorite bands.</p>

Use an  if  Statement in a Template

You can also use branching logic in your templates.

Underneath our list of bands, let’s add the following code in our template:

<p>
    I have
    {% if bands|length < 5 %}
        a few
    {% elif bands|length < 10 %}
        some
    {% else %}
        many
    {% endif %}
        favourite bands.
</p>

 if , elif , and  else  statements are also similar to Python - but again, omit the trailing colon (: ). 

Here’s what those last two examples would look like in the browser:

The web page displays I have 3 favourite bands. I have a few favourite bands.
The if statement rendered.

Now that you’ve followed along, you can check your understanding by watching this screencast, which goes through the whole process of creating a template.

Now You Try! Create Django Templates for Your Pages

Now that you understand the importance of templates and you’ve seen some of the useful things you can do in them, it’s time for you to create some templates of your own.

Create templates for the  listings  view, the  about  view, and the  contact  view that you created in the last chapter. Your templates should be full HTML pages, including   <html> ,  <head> ,  <title> , and  <body>  tags. And for the  listings  view, you should inject your  Listing  objects into the template. 

Remember that you’ll need to do the following for each template you create:

  • Create a template file in listings/templates/listings/’ and give it the ‘.html’ extension.

  • Move your HTML out of the view and into the template.

  • Change the view’s return statement to call the  render  method and pass it the path of your template file.

  • Also pass a context dictionary to the  render  method.

  • Use template variables to inject data into your template.

  • Use template tags to use looping in your template where appropriate.

Before we move onto the next chapter, I want you to take a look at the two template files you’ve created. Do you see any repeated code in them?

Let’s Recap!

  • Templates are where you define all the presentational elements of a page - in a web app, that’s the HTML.

  • This leaves the view to focus on logic - including fetching the correct data to inject into the page. 

  • Inject data into a template using template variables.

  • Use template tags for looping, branching, and formatting in templates. 

Now we have our templates - the ‘T’ in MVT architecture - how can we avoid repeating code between templates? The answer is a base template, and we’ll look at that in the next chapter.

Example of certificate of achievement
Example of certificate of achievement