Basics of Django Templates
Last updated on July 27, 2020
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #...
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.
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 variablepost_title
insideh1
tag. So if the value ofpost_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.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, thefor
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 useif
template tag to add logical if statement to our template. In line 21, we have anif
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 isTrue
then<p>This post has some comments</p>
will be printed otherwise,<p>This post has no comments</p>
will be printed.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 variableauthor
is"bob"
then because of thetitle
filter"Bob"
will be printed instead of"bob"
. Thetitle
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:
- Create a new
Template
object by passing the content of the template as a raw string. - Call
render()
method on template object with a given set of variables. Therender()
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.
1 2 3 4 5 6 | (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.
1 2 3 4 5 6 7 | >>>
>>> 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.
In line 2, we are importing
template
module from thedjango
package.The
template
module hasTemplate
class. In line 3, we are creating a newTemplate
object by passing a raw string to the constructor ofTemplate
class.Our template string consists of one variable. To pass the value to this variable we use
Context
class. InstantiateContext
class by passing a dictionary mapping variable names to variable values.Finally, to render the template call
render()
method withContext
object as a parameter.
Once the template is loaded, it can be rendered as many times as you want to without reloading.
1 2 3 4 5 | >>>
>>> 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.
1 2 3 4 5 | >>>
>>> 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.
1 2 3 4 5 | >>>
>>> 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 #
1 2 3 4 5 6 7 8 9 10 11 12 13 | >>>
>>> 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 #
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | >>>
>>> 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 ()
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | >>>
>>> 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.
1 2 3 4 5 6 7 | >>>
>>> 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.
- Dictionary lookup -
var.['key']
- Attribute lookup -
var.key
- Method call lookup -
var.key()
- 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.
Load Comments