OverIQ.com

Loading Templates in Django

Last updated on July 27, 2020


In the last few chapters, we have learned quite a lot about Django templates. In this lesson, we will put some of the things to use. Open views.py in the blog app located at TGDB/django_project/blog. At this point, the file should look like this:

TGDB/django_project/blog/views.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from django.http import HttpResponse
import datetime


def index(request):
    return HttpResponse("Hello Django")


def today_is(request):
    now = datetime.datetime.now()
    html = "<html><body>Current date and time {0}</body></html>".format(now)
    return HttpResponse(html)

Let's update today_is() view to use templates. Modify views.py as follows:

TGDB/django_project/blog/views.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from django.http import HttpResponse
import datetime
from django import template

...

def today_is(request):
    now = datetime.datetime.now()
    t = template.Template("<html><body>Current date and time {{ now }}</body></html>")
    c = template.Context({'now': now})
    html = t.render(c)
    return HttpResponse(html)

Start the server and if not already running and visit http://127.0.0.1:8000/blog/time/, you should get the current date and time, just like before.

Definitely, we are using Django templates but there is still one problem though. We are still hardcoding raw HTML inside our views. Creating templates for a large HTML page in this way would be very cumbersome and tedious. It would be much better if we could write the HTML in an external file. Let's do this.

Recall from Basics of Django templates lesson that by default Django searches for templates inside the templates directory inside every installed app. Create a new file called datetime.html inside blog/templates/blog directory and add the following code to it.

TGDB/django_project/blog/templates/blog/datetime.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Current Time</title>
</head>
<body>

    {# This is a comment #}

    {# check the existence of now variable in the template using if tag #}

    {% if now %}   
        <p>Current date and time is {{ now }}</p>
    {% else %}               
        <p>now variable is not available</p>
    {% endif %}

</body>
</html>

Okay, now we have created a template, the next question is how to load this template inside our view. It turns out that the template module provides a class called loader which in turn provides a get_template() method to load the template. The get_template() method takes a string argument indicating the name of the template, figures out where the template is, opens the file and returns a Template object. If get_template() cannot find the template, it raise TemplateDoesNotExist exception.

Open views.py and amend today_is() view function as follows:

TGDB/django_project/blog/views.py

1
2
3
4
5
6
7
#...
def today_is(request):
    now = datetime.datetime.now()
    t = template.loader.get_template('blog/datetime.html')
    c = template.Context({'now': now})
    html = t.render(c)
    return HttpResponse(html)

Let's step through the changes we have made in our code.

In line 4, we are loading our template using get_loader() method of the loader class. Notice that we are passing only name of the app, followed by a slash (/), followed by the name of the template i.e datetime.html instead of the full path to datetime.html which is blog/templates/blog/datetime.html. This is because we are using Django convention of storing templates in the templates directory of the blog app. As a result, Django will automatically figure out where the template is.

Similarly, if we had an app called forums and a template discussions.html inside forums/templates/forums/discussions.html. Then we would be loading our template using forums/discussions.html.

Rest of the code works as usual. To check whether you have done everything correctly or not, visit http://127.0.0.1:8000/blog/time/ again. You will get the current date and time.

If get_template() function can't find the template, it will throw TemplateDoesNotExist exception. To view TemplateDoesNotExist error modify the today_is() view function as follows:

TGDB/django_project/blog/views.py

1
2
3
4
5
6
7
#...
def today_is(request):
    now = datetime.datetime.now()
    t = template.loader.get_template('blog/datetimeeeeee.html')
    c = template.Context({'now': now})
    html = t.render(c)
    return HttpResponse(html)

Open the browser and visit http://127.0.0.1:8000/blog/time/. You will get output similar to the following:

You will encounter errors like these many times while developing web apps in Django. The important thing to notice here is the "Template-loader postmortem" section. This section tells you a list of directories where Django template system tried to find the file before throwing TemplateDoesNotExist exception. This information may prove extremely valuable while debugging the cause of the error.

Before moving on let's change blog/datetimeeeeee.html to blog/datetime.html. Open a browser and visit http://127.0.0.1:8000/blog/time/ again, the error should have gone.

Shortening the code using render_to_response() #

Most of the time a view does the following task:

  1. Pull data from the database using models (we will discuss Models in Basics of Models in Django).
  2. Load the template file and create Template object.
  3. Create Context object to supply data to the template.
  4. Call render() method.
  5. Create HttpResponse() and send it to the client.

Django provides a function called render_to_response() to do all things mentioned above from step 2 to 5. It accepts two arguments template name and a dictionary (which will be used to create Context object). To use render_to_response() you must first import it from django.shortcuts module.

Modify today_is() view function in views.py file to use render_to_response() method as follows:

TGDB/django_project/blog/views.py

1
2
3
4
5
6
7
8
9
from django.http import HttpResponse
import datetime
from django.shortcuts import render_to_response

#...

def today_is(request):
    now = datetime.datetime.now()
    return render_to_response('blog/datetime.html', {'now': now })

Refresh the page at http://127.0.0.1:8000/blog/time/ and Django will greet you with current date and time again.

The render() function #

The render() function works similar to render_to_response() but it provides some additional variables inside the Django templates (although there are many other subtle differences between render() and render_to_response() but for now we will not going to discuss them). One such variable is request which is an object of type HttpRequest. Recall that every view function accepts request object as a first parameter. If you want to access request object inside templates you must use render() instead of render_to_response(). At this point, we can't do anything useful with request object but just to give you an example, we will try to get the scheme used to access the web page. Open datetime.html template and make the following changes:

TGDB/django_project/blog/templates/blog/datetime.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Current Time</title>
</head>
<body>

    {# This is a comment #}

    {# check the existence of now variable in the template using if tag #}

    {% if now %}   
        <p>Current date and time is {{ now }}</p>
    {% else %}               
        <p>now variable is not available</p>
    {% endif %}

    <p>Current scheme: {{ request.scheme }}</p>

</body>
</html>

The request object has an attribute called scheme which returns the scheme of the request. In other words, if the page is requested using the http then scheme is http. On the other hand, if it is requested using https then scheme is https.

Open the browser and visit http://127.0.0.1:8000/blog/time/ you will get the following output.

Notice that nothing is printed in the place of request.scheme because we are using render_to_response(). To use render() function first import it from django.shortcuts module. Let's update today_is() view function to use render() instead of render_to_response() function.

TGDB/django_project/blog/views.py

1
2
3
4
5
6
7
8
9
from django.http import HttpResponse
import datetime
from django.shortcuts import render

#...

def today_is(request):
    now = datetime.datetime.now()    
    return render(request, 'blog/datetime.html', {'now': now})

The render() function accepts an additional first argument called request. Refresh the page and you will get the following output:

Important Note: render_to_response() is deprecated and will be removed from the Django in the future. So, Django documentation recommends that you should always use render() instead of render_to_response().

Note: To checkout this version of the repository type git checkout 9a.