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 turn off this behavior open settings.py and set 'APP_DIRS' value to
False like this.

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. Thus 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:

<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 us surrounded by double curly braces ( {{ post_title }} ) is a variable, for example, the code in line 7 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> would be printed. You might say but how do we specify value of the variable ? Don't worry we will get to that later.
  2. Text surrounded by a single curly brace ({) and 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 20, 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> would be printed otherwise, <p>This post has no comments</p> would be printed.

  3. Finally in line 9, i.e {{ author|title }}, we have used a filter. A filter is a way to format the output of the variable. We specify filter using (|) pipe character followed by 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 has 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 #

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 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 1, we are importing template module from django.

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

  3. At this point we have one variable in our template raw string. To pass values to the template we use Context class. The template module provides Context class, before we can pass variables to our templates we have to instantiate a Context object. To instantiate Context object just pass a dictionary mapping variable names to variable values.

  4. Finally, to render the template call render() method with Context object as 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 website to become inaccessible 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 access 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 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 objects inside the templates, but remember that while calling a method 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 dot (.) 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 negative index in python to access elements of a list, but this functionality is not available inside Django templates.

Order of the dot lookups #

The dot(.) character has a special meaning inside the templates. A dot(.) inside 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 then capitalize() method will be called on it.