Django Templates
In my last article (February 2015), I explained how to create a simple Django project ("atfproject") and inside that, create a simple application (atfapp). The application worked in that if you went to the URL http://localhost:8000/hello/Reuven, you got the text "hello, Reuven".
This was thanks to a combination of the view function:
def hello(request, name):
return HttpResponse("hello, {}".format(name))
and the URL pattern that I created:
urlpatterns = patterns('',
url(r'^hello/(?P<name>\w+)$', hello),
url(r'^admin/', include(admin.site.urls)),
)
Notice that in the first URL pattern, I define a named group,
"name", as a string of alphanumeric characters
(\w+
). The captured
characters then are passed to the view function's "name" parameter,
which allows the view function to accept and display the values within
the view function.
Now, this does work, but if you're thinking that this is a pretty awkward way to display text, as a string within a view function, you're not alone. Indeed, aside from producing extremely small pieces of text, you're likely never going to return HTML directly from a view function. Instead, you'll use a template.
This shouldn't come as a surprise to anyone who has been doing Web development for any length of time. Every Web framework I have used has some form of templates. Unfortunately, every Web framework uses a unique type of template, with a new and different way to integrate HTML and the sorts of dynamic content that you need to present.
So in this article, I describe how Django's templates work, allowing you to generate dynamic content for your users.
Invoking TemplatesIt's important to remember that Django's templates are HTML files with a bit of extra code thrown in. And even saying that there is "code" in Django templates probably is exaggerating things a bit. The template syntax is designed explicitly to reduce the amount of code that you have to write, but also to reduce the amount of code that is executed in the template. By removing code from the template and putting it into your view methods (and models), you make your application more modular, more flexible and more testable.
To start with Django templates, you don't need to know anything
special. That's because a plain-old HTML file is a fine Django
template. Inside the "atfapp" application, let's create a new
subdirectory called templates. This is where Django will look for
your templates. You always can configure this differently by setting
the TEMPLATE_DIRS
variable inside the application's settings.
Here is a simple template that I created and then put inside atfapp/templates/hello.html:
<!DOCTYPE html>
<html>
<head>
<h3>Hello!</h3>
</head>
<body>
<h1>Hello!</h1>
<p>Hello out there!</p>
</body>
</html>
In order to get Django to display this template, you need to change
your "hello" view function (defined in your application's views.py) such
that it renders its response using the template. The easiest way to
do that is to use the render_to_response
function, defined in the
django_shortcuts
package. Thus, change views.py to read as
follows:
from django.shortcuts import render
from django.http import HttpResponse
from django.shortcuts import render_to_response
def hello(request, name):
return render_to_response('hello.html')
Notice that it isn't enough to invoke
render_to_response
. As with all
functions in Python, you must explicitly return the object that
render_to_response
returned to you.
With the template in place and the view function updated, you now can
reload the application at http://localhost:8000/hello/Reuven.
And...well, you'll probably see a debugging screen from Django,
telling you that the template wasn't found. The problem here is that
when you use render_to_response
, it looks in the template directories
of all of the registered Django applications within the project. But
wait, you never registered your application! Thus, although you can invoke
view functions from within atfapp, Django won't look in the
atfapp/templates directory, because it's not a registered app.
The simple solution is to go to settings.py in the main project's
configuration directory (atfproject/atfproject/settings.py, in this
case), find the definition of INSTALLED_APPS
, and then add the
following line to the end:
'atfapp'
Note that you'll have to add a comma to the end of the previous line.
With this in place, Django's template system will find your template. Going to /hello/Reuven (or any other URL under /hello/) now will display your template.
Passing VariablesOf course, this basic "hello" template isn't really demonstrating the power of a Web application. In order for that to happen, you're going to need to pass values to the template, which then will mix your values with the HTML and display the result to the user.
So, you need to do two things. First,
you need to change your invocation of
render_to_response
, such that it
passes one or more variable values. If you are at all familiar with
Python, you won't be surprised to discover that you will do this
by passing a dictionary to render_to_response
, in which the keys are
the variable names you want to pass. For example:
def hello(request, name):
return render_to_response('hello.html', {'name':name})
In this example, you take the parameter "name", which was assigned via the URL, and pass it as the value in your dictionary. The key is called "name", which I admit can be a tiny bit confusing, but it still makes the most sense here.
In your template, Django looks for double curly braces: {{ and }}. Django will look inside those braces and look for the name in the dictionary that it was passed:
<!DOCTYPE html>
<html>
<head>
<h3>Hello!</h3>
</head>
<body>
<h1>Hello!</h1>
<p>Hello, {{name}}!</p>
</body>
</html>
With just these two changes in place—and without even having to restart the server—the contents of your URL now affect the template's output.
You can pass any number of name-value pairs in the dictionary defined
in render_to_response
. The passed values might come from arguments
passed to the view function, from the database or from a remote
server. From the template's perspective, it has access only to the
data that was passed to it and doesn't really care where the rest of
the data came from.
Of course, there are times when you might want to have text appear conditionally. This also is possible with Django templates. Instead of using {{ and }} around variable names, you can use {% and %} around commands. Now, these are not Python commands, so don't expect the syntax, names or behavior to be identical. Also, because you don't have Python's indented block syntax, you must end your "if" condition (for example) with an "endif" tag.
Given that information, you probably can figure out what happens in this template:
<!DOCTYPE html>
<html>
<head>
<h3>Hello!</h3>
</head>
<body>
<h1>Hello!</h1>
{% if name == 'Reuven' %}
<p>Hello, master {{name}}!</p>
{% else %}
<p>Hello, {{name}}!</p>
{% endif %}
</body>
</html>
The template gets a parameter "name", which it then compares with the string "Reuven". If I'm the named person, it prints one message. Otherwise, it prints another message.
Loops and FiltersThe previous example shows what it looks like when you take a value from a URL and then want to pass it to a template for display. Parameters to view functions always are going to be passed as strings. However, there is no reason why you can't pass another data structure, such as a list, tuple or dict. If you do this (or, to be honest, if you pass any iterable), you use the template's looping function, which operates identically to Python's "for" operator, but with the addition of a closing "endfor" tag.
Let's change the view function to work as follows:
def hello(request, name):
return render_to_response('hello.html', {'name':name,
'children': ['Atara', 'Shikma', 'Amotz']})
As you can see, you're now going to pass a section variable to your template, containing my children's first names. Inside your template, you can iterate over this variable, almost precisely as you would within a non-Django, non-template Python program:
<!DOCTYPE html>
<html>
<head>
<h3>Hello!</h3>
</head>
<body>
<h1>Hello!</h1>
{% if name == 'Reuven' %}
<p>Hello, master {{name}}!</p>
{% else %}
<p>Hello, {{name}}!</p>
{% endif %}
<h2>Children</h2>
<ol>
{% for child in children %}
<li>{{child}}</li>
{% endfor %}
</ol>
</body>
</html>
In this example, you have combined HTML's "ol" tag to provide an enumerated list, along with a "for" loop in Django's templates. Because "child" is defined as a variable within the loop, you can use the {{child}} syntax to indicate where you want the child's name to appear.
Now, if you're printing a list of names, it's possible that the strings have become all lowercase. Let's say you would like to ensure that the names follow English-language rules, in which the first character is capitalized. Now, if you were using straight Python, you could use the str.upper method, as in:
<li>{{child|capfirst}}</li>
But, if you change the children's names to lowercase and then change the template to look as it does above...well, let's just say that it won't work. This is part of Django's philosophy of keeping executable code outside the templates. Consider this: it shouldn't be possible, or at least easy, for someone to run arbitrary code inside your templates.
Thus, if you want to capitalize the words, you'll need to use a "filter", Django's term for a predefined function to which you can pass your data and which then will return a new string that will be displayed. For example, the "capfirst" filter capitalizes the first letter of a word:
<li>{{child|capfirst}}</li>
Notice the structure of the filtered variable. After the variable name itself, you use the | character and then the name of the filter you want to use. Django actually comes with a huge number of predefined filters and also allows you to write and register your own filters. But for most day-to-day display needs, Django's built-in filters probably will be sufficient.
ConclusionUsing one or more templates from within Django is quite easy, employing a syntax that is different from many other frameworks but still workable and easy to understand. One of the features I didn't discuss here is that of "blocks", areas of HTML that are replaced with text that comes from a child template. In this way, you can display different values in the page title or h1, but on a page-by-page basis. I'll cover more of this in coming articles about Django.
In my next article, however, I plan to take a look at how Django can work with a database and thus create a true CRUD (that is, create-read-update-destroy) Web-database application.
ResourcesThe main site for Django is http://DjangoProject.com, and it provides a great deal of excellent documentation, including a tutorial. Information about Python, in which Django is implemented, is at http://python.org.