LJ Archive

At the Forge

Django Templates

Reuven M. Lerner

Issue #252, April 2015

Displaying dynamic data with Django's template language.

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 Templates

It'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>
    <title>Hello!</title>
  </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 Variables

Of 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>
    <title>Hello!</title>
  </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>
    <title>Hello!</title>
  </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 Filters

The 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>
    <title>Hello!</title>
  </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.

Conclusion

Using 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.

Reuven M. Lerner is a Web developer, consultant and trainer. He recently completed his PhD in Learning Sciences from Northwestern University. You can read his blog, Twitter feed and newsletter at lerner.co.il. Reuven lives with his wife and three children in Modi'in, Israel.

LJ Archive