Basics of Django Templates

What are Django Templates? #

Think of Django templates as a scaffolding required to create a complete HTML page. Django templates are nothing more than a text file containing static content and as well as a special dynamic markup specifying some logic, looping, and display of data.

Where to store Django Templates? #

Before we start building templates let's first spend some time to learn how Django searches for templates. Create a new directory called templates inside the blog app located at TGDB/django_project/blog. But why templates directory? Because by default Django searches for templates in the templates directory of every installed app.

If for some reason you want to turn off this behavior open settings.py and set 'APP_DIRS' value to False like this:

TGDB/django_project/django_project/settings.py

#...
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': False,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
#...

For now, we don't have any sound reason to turn off this behavior, so again switch it back to True.

Django treats all of the templates directories as a single directory. That being said, let's say you have two apps blog and forum with blog/templates/list.html and forum/templates/list.html templates respectively. To Django these two files are same and It will use the template it finds first. Due to this behavior, Django recommends creating a subdirectory inside the templates directory by the name of the app. Create a new subdirectory called blog inside the templates directory. This is where we will store all our templates for the blog app.

A simple Django Template #

To make things clear let's take an example of Django templates. Here is a simple template:

<!DOCTYPE html>
<html>
    <head>
        <title>Blog Post</title>
    </head>

    <body>
    <h1>{{ post_title }}</h1>

    <p>Published by <span>Author : {{ author|title }} </span></p>
    
    <p>{{ post_content }}</p>

    <p>Related Posts:</p>
    <ul>
        {% for item in item_list %}
            <li>{{ item }}</li>
        {% endfor %}
    </ul>

    {% if comments %}
        <p>This post has some comments</p>
    {% else %}
        <p>This post has no comments</p>
    {% endif %}

    </body>
</html>

Notice that in addition to HTML code we are using special markup to denote dynamic sections of the page.

Let's step through the code.

  1. Any text that is surrounded by double curly braces ( {{ post_title }} ) is a variable, for example, the code in line 8 i.e <h1>{{ post_title }}</h1> means that output the value of the variable post_title inside h1 tag. So if the value of post_title is "Django Templates" then <h1>Django Templates</h1> will be printed. You might say but how do we specify the value of the variable? Don't worry we will get to that later.

  2. Text surrounded by a single curly brace ({) and the percent sign (%), for example, in line 16 i.e {% for item in item_list %} is called a template tag. A template tag allows us to something very specific. In this case, the for tag works very much like a for loop in Python and is generally used to loop over a list or dictionary.

    Just as we can emulate a loop in our templates using the for template tag. We can use if template tag to add logical if statement to our template. In line 21, we have an if template tag.

    {% if comments %}
        <p>This post has { comment.count } comments</p>
    {% else %}
        <p>This post has no comments</p>
    {% endif %}

    Here is how it works:

    First, the value of variable comments is evaluated. If it is True then <p>This post has some comments</p> will be printed otherwise, <p>This post has no comments</p> will be printed.

  3. Finally, in line 10, we have used a filter i.e {{ author|title }}. A filter is a way to format the output of the variable. We specify the filter using the pipe character (|) followed by the name of the filter after the variable name. If the value of variable author is "bob" then because of the title filter "Bob" will be printed instead of "bob". The title filter converts the first character of every word in the string to uppercase and remaining characters to the lowercase.

Django templates have access to many other filters; we will discuss some important ones in lesson Templates filter in Django. In addition to built-in filters, you can also create your own filters.

Using Django Template System #

The template engine is software which loads and renders a template into markup. Each template engine defines a language to create templates. We have already seen some part of this language in the previous section i.e {{ post_title }} and {% if comments %}, Remember these ?? The template engine provided by Django is called Django Template Language or DTL for short. Before Django 1.8, DTL was the only option. But with Django 1.8 and later we can use an external template engine like Jinja2. However, if you don't have any sound reason to use Jinja2 just stick to DTL and you would be fine. Throughout this tutorial, we would be using DTL only.

Before we start to use templates in our views let's dig in a little deeper to understand the mechanics of how they work. Here is the basic workflow involved in using Django templates:

  1. Create a new Template object by passing the content of the template as a raw string.
  2. Call render() method on template object with a given set of variables. The render() method returns a fully rendered code after evaluating all variables and template tags.

To create Template objects open interactive Python shell by issuing the following command.

(env) C:\Users\Q\TGDB\django_project>python manage.py shell
Python 3.4.4 (v3.4.4:737efcadf5a6, Dec 20 2015, 20:20:57) [MSC v.1600 64 bit (AM
D64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>

Noticed something different here? Well, here we are starting Python shell using the python manage.py shell instead of just python. But why? Because in addition to invoking Python interpreter python manage.py shell imports bare minimum of Django to work with. We will refer to this shell as Django shell.

Okay, let's create some Template objects now.

>>>
>>> from django import template
>>> t = template.Template("We are learning {{ name }}")
>>> c = template.Context({'name': 'Django'})
>>> print(t.render(c))
We are learning Django
>>>

Here is how the above code works.

  1. In line 2, we are importing template module from the django package.

  2. The template module has Template class. In line 3, we are creating a new Template object by passing a raw string to the constructor of Template class.

  3. Our template string consists of one variable. To pass the value to this variable we use Context class. Instantiate Context class by passing a dictionary mapping variable names to variable values.

  4. Finally, to render the template call render() method with Context object as a parameter.

Once the template is loaded, it can be rendered as many times as you want to without reloading.

>>>
>>> c = template.Context({'name': 'a new web framework'})
>>> print(t.render(c))
We are learning a new web framework
>>>

But what would happen if we don't provide any value to the Context constructor? In that case, you would not get any error message instead Django will print nothing. This may sound frustrating at first but it is very useful because, in a real-world application, it's unacceptable for a website to start throwing errors just because of a missing value.

>>>
>>> c = template.Context()
>>> print(t.render(c))
We are learning
>>>

The keys in the Context dictionary is case sensitive, so Django will print nothing if the variable name is slightly different from the expected variable name in the template.

>>>
>>> c = template.Context({'Name': 'Django framework'})
>>> print(t.render(c))
We are learning
>>>

Therefore it is very important to make sure that the name of keys in the Context dictionary is exactly the same as in templates.

Context Variable Lookup #

So far we have been passing simple data mainly strings to our templates. However, Django template system can easily handle complex data structures like list, dictionary or even objects. The key to accessing these complex data structures inside the templates is to use the dot character (.). This can be best described by some examples.

Passing a dictionary #

>>>
>>> framework = {
... 'first': 'Django',
... 'second': 'Laravel',
... 'third': 'Spring',
... 'fourth': 'CodeIgniter'
... }
>>>
>>> t = template.Template("We are learning {{ framework.first }}")
>>> c = template.Context({'framework': framework})
>>> print(t.render(c))
We are learning Django
>>>

Similarly, we can use the dot operator (.) to access objects attributes.

Passing an object #

>>>
>>> import datetime
>>> now = datetime.datetime.now()
>>> now.day
25
>>> now.month
1
>>> now.year
2017
>>>
>>>    
>>> t = template.Template("Date is {{ now.day }}-{{ now.month }}-{{ now.year }}")
>>> c = template.Context({'now':now})
>>> print(t.render(c))
Date is 25-1-2017
>>>

In the above example, we are using a built-in class. The same behavior is true for custom classes we create.

Calling methods #

Using the dot operator (.) you can also call methods of an object inside the templates, but remember that while doing so we do not include parentheses ().

>>>
>>> name = "django"
>>> name.capitalize()
'Django'
>>> name.upper()
'DJANGO'
>>> name
'django'
>>>
>>> t = template.Template("{{ var.capitalize }} learning {{ var.upper }}")
>>> c = template.Context({'var': name})
>>> print(t.render(c))
Django learning DJANGO
>>>

It is important to note that we can only call methods inside the template which do not require any arguments.

Passing a list #

We can also access elements of a list using the dot (.) operator followed by the index position. Remember that lists in python are 0 indexed.

>>>
>>> list = ['Ruby on Rails', 'Django', 'Laravel']
>>> t = template.Template("We are learning {{ list.1 }} ")
>>> c = template.Context({'list': list})
>>> print(t.render(c))
We are learning Django
>>>

Although, we can use a negative index in Python to access elements of a list, but this functionality is not available in Django templates.

Order of the dot lookups #

The dot (.) character has a special meaning inside the templates. A dot (.) after a variable name signifies lookup. When Django template system encounters a dot (.) after a variable name, it tries the perform lookups, in this particular order.

  1. Dictionary lookup - var.['key']
  2. Attribute lookup - var.key
  3. Method call lookup - var.key()
  4. List-index lookup - var.1

The template system uses the first lookup that works. It's is short-circuit logic. In other words, we can chain lookups one after another. For example: {{ person.first_name.capitalize }}, since lookups work using short-circuit logic first person.first_name is evaluated followed by the capitalize() method.