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
from the djangobin app located at djangobin/django_project/djangobin
. At this point, the file should look like this:
djangobin/django_project/djangobin/views.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | from django.shortcuts import HttpResponse
import datetime
def index(request):
return HttpResponse("<p>Hello Django</p>")
def today_is(request, username):
now = datetime.datetime.now()
html = "<html><body>Current date and time: {0}</body></html>".format(now)
return HttpResponse(html)
def profile(request, username):
return HttpResponse("<p>Profile page of #{}</p>".format(username))
def book_category(request, category='sci-fi'):
return HttpResponse("<p>Books in {} category</p>".format(category))
def extra_args(request, arg1=None, arg2=None):
return HttpResponse("<p>arg1: {} <br> arg2: {} </p>".format(arg1, arg2))
|
Let's update today_is()
view to use template as follows:
djangobin/django_project/djangobin/views.py
1 2 3 4 5 6 7 8 9 10 11 12 | from django.shortcuts 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, if not already running and visit http://127.0.0.1:8000/time/
. You should get the current date and time, just like before.
Definitely, we are using Django templates but 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 of every installed app. Create a new file called datetime.html
inside djangobin/templates/djangobin
directory and then add the following code to it.
djangobin/django_project/djangobin/templates/djangobin/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
package provides a module called loader
which in turn provides a get_template()
function to load the template. The get_template()
function 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 raises a TemplateDoesNotExist
exception.
Open views.py
and amend today_is()
view function as follows:
djangobin/django_project/djangobin/views.py
1 2 3 4 5 6 7 8 | #...
def today_is(request):
now = datetime.datetime.now()
t = template.loader.get_template('djangobin/datetime.html')
c = template.Context({'now': now})
html = t.render(c)
return HttpResponse(html)
|
Notice that in line 5, we are just passing the name of the app, followed by a slash (/
), followed by the template name ( i.e datetime.html
) to the get_template()
function instead of the full path to datetime.html
which is djangobin/templates/djangobin/datetime.html
. This is because we are using Django convention of storing templates. As a result, Django will automatically figure out where the template is.
Similarly, if we had an app called forums and a template named discussions.html
inside forums/templates/forums/discussions.html
. Then we would be loading discussions.html
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/time/
again. You will get TypeError
exception like this:
Don't get frightened. The problem is that the Context
class is deprecated in Django 1.11. To fix the issue simply pass a dictionary mapping variable name to value to the render()
method. However, if you create django.template.Template
objects manually, then you will still have to pass Context
instance to the render()
method (odd but true). Here is the updated today_is()
view function.
djangobin/django_project/djangobin/views.py
1 2 3 4 5 6 7 | #...
def today_is(request):
now = datetime.datetime.now()
t = template.loader.get_template('djangobin/datetime.html')
html = t.render({'now': now})
return HttpResponse(html)
|
Visit http://127.0.0.1:8000/time/
again and you will see the current date and time just as before.
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 :
djangobin/django_project/djangobin/views.py
1 2 3 4 5 6 7 | #...
def today_is(request):
now = datetime.datetime.now()
t = template.loader.get_template('djangobin/datetimeeeeee.html')
html = t.render({'now': now})
return HttpResponse(html)
|
Open the browser and visit http://127.0.0.1:8000/djangobin/time/
. You will get TemplateDoesNotExist
exception as follows:
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 djangobin/datetimeeeeee.html
to djangobin/datetime.html
. Open a browser and visit http://127.0.0.1:8000/djangobin/time/
again, the error should have gone.
Shortening the code using render_to_response() #
Most of the time a view does the following task:
- Pull data from the database using models (we will discuss Models in Basics of Models in Django).
- Load the template file and create
Template
object. - Call
render()
method to render the template. - Create
HttpResponse()
object and send it to the client.
Django provides a function called render_to_response()
to do all things mentioned 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:
djangobin/django_project/djangobin/views.py
1 2 3 4 5 6 7 8 | from django.shortcuts import HttpResponse, render_to_response
import datetime
#...
def today_is(request):
now = datetime.datetime.now()
return render_to_response('djangobin/datetime.html', {'now': now })
|
Refresh the page at http://127.0.0.1:8000/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 makes some additional variables automatically available inside the Django templates. 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:
djangobin/django_project/djangobin/templates/djangobin/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/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, then update today_is()
view function to use render()
function as follows:
djangobin/django_project/djangobin/views.py
1 2 3 4 5 6 7 8 | from django.shortcuts import HttpResponse, render
import datetime
#...
def today_is(request):
now = datetime.datetime.now()
return render(request, 'djangobin/datetime.html', {'now': now})
|
The render()
function accepts an additional first argument called request
. Refresh the page and you will get the following output:
In your code you should always use render()
instead of render_to_response()
because there is possibility that render_to_response()
will be deprecated in the future.
Setting Content-Type and HTTP Status #
By default, the render()
shortcut creates a HttpResponse
object with Content-Type: text/html
and HTTP Status of 200.
We can override Content-Type
header and HTTP Status using content_type
and status
keyword arguments respectively. For example:
1 2 | def my_view(request):
return render(request,'myapp/markdown.md', context_dict, content_type=`text/markdown`)
|
This returns a HttpResponse
object with text/markdown
content type and default HTTP status of 200 OK.
1 2 | def my_view(request):
return render(request,'myapp/404.html', context_dict, status=404)
|
This returns a HttpResponse
object with text/html
content type and HTTP status of 404 Not Found.
1 2 | def my_view(request):
return render(request,'myapp/404.html', context_dict, content_type='application/json', status=405)
|
This returns a HttpResponse
object with application/json
content type and HTTP status of 404 METHOD NOT ALLOWED.
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
from the djangobin app located at djangobin/django_project/djangobin
. At this point, the file should look like this:
djangobin/django_project/djangobin/views.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | from django.shortcuts import HttpResponse
import datetime
def index(request):
return HttpResponse("<p>Hello Django</p>")
def today_is(request, username):
now = datetime.datetime.now()
html = "<html><body>Current date and time: {0}</body></html>".format(now)
return HttpResponse(html)
def profile(request, username):
return HttpResponse("<p>Profile page of #{}</p>".format(username))
def book_category(request, category='sci-fi'):
return HttpResponse("<p>Books in {} category</p>".format(category))
def extra_args(request, arg1=None, arg2=None):
return HttpResponse("<p>arg1: {} <br> arg2: {} </p>".format(arg1, arg2))
|
Let's update today_is()
view to use template as follows:
djangobin/django_project/djangobin/views.py
1 2 3 4 5 6 7 8 9 10 11 12 | from django.shortcuts 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, if not already running and visit http://127.0.0.1:8000/time/
. You should get the current date and time, just like before.
Definitely, we are using Django templates but 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 of every installed app. Create a new file called datetime.html
inside djangobin/templates/djangobin
directory and then add the following code to it.
djangobin/django_project/djangobin/templates/djangobin/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
package provides a module called loader
which in turn provides a get_template()
function to load the template. The get_template()
function 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 raises a TemplateDoesNotExist
exception.
Open views.py
and amend today_is()
view function as follows:
djangobin/django_project/djangobin/views.py
1 2 3 4 5 6 7 8 | #...
def today_is(request):
now = datetime.datetime.now()
t = template.loader.get_template('djangobin/datetime.html')
c = template.Context({'now': now})
html = t.render(c)
return HttpResponse(html)
|
Notice that in line 5, we are just passing the name of the app, followed by a slash (/
), followed by the template name ( i.e datetime.html
) to the get_template()
function instead of the full path to datetime.html
which is djangobin/templates/djangobin/datetime.html
. This is because we are using Django convention of storing templates. As a result, Django will automatically figure out where the template is.
Similarly, if we had an app called forums and a template named discussions.html
inside forums/templates/forums/discussions.html
. Then we would be loading discussions.html
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/time/
again. You will get TypeError
exception like this:
Don't get frightened. The problem is that the Context
class is deprecated in Django 1.11. To fix the issue simply pass a dictionary mapping variable name to value to the render()
method. However, if you create django.template.Template
objects manually, then you will still have to pass Context
instance to the render()
method (odd but true). Here is the updated today_is()
view function.
djangobin/django_project/djangobin/views.py
1 2 3 4 5 6 7 | #...
def today_is(request):
now = datetime.datetime.now()
t = template.loader.get_template('djangobin/datetime.html')
html = t.render({'now': now})
return HttpResponse(html)
|
Visit http://127.0.0.1:8000/time/
again and you will see the current date and time just as before.
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 :
djangobin/django_project/djangobin/views.py
1 2 3 4 5 6 7 | #...
def today_is(request):
now = datetime.datetime.now()
t = template.loader.get_template('djangobin/datetimeeeeee.html')
html = t.render({'now': now})
return HttpResponse(html)
|
Open the browser and visit http://127.0.0.1:8000/djangobin/time/
. You will get TemplateDoesNotExist
exception as follows:
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 djangobin/datetimeeeeee.html
to djangobin/datetime.html
. Open a browser and visit http://127.0.0.1:8000/djangobin/time/
again, the error should have gone.
Shortening the code using render_to_response() #
Most of the time a view does the following task:
- Pull data from the database using models (we will discuss Models in Basics of Models in Django).
- Load the template file and create
Template
object. - Call
render()
method to render the template. - Create
HttpResponse()
object and send it to the client.
Django provides a function called render_to_response()
to do all things mentioned 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:
djangobin/django_project/djangobin/views.py
1 2 3 4 5 6 7 8 | from django.shortcuts import HttpResponse, render_to_response
import datetime
#...
def today_is(request):
now = datetime.datetime.now()
return render_to_response('djangobin/datetime.html', {'now': now })
|
Refresh the page at http://127.0.0.1:8000/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 makes some additional variables automatically available inside the Django templates. 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:
djangobin/django_project/djangobin/templates/djangobin/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/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, then update today_is()
view function to use render()
function as follows:
djangobin/django_project/djangobin/views.py
1 2 3 4 5 6 7 8 | from django.shortcuts import HttpResponse, render
import datetime
#...
def today_is(request):
now = datetime.datetime.now()
return render(request, 'djangobin/datetime.html', {'now': now})
|
The render()
function accepts an additional first argument called request
. Refresh the page and you will get the following output:
In your code you should always use render()
instead of render_to_response()
because there is possibility that render_to_response()
will be deprecated in the future.
Setting Content-Type and HTTP Status #
By default, the render()
shortcut creates a HttpResponse
object with Content-Type: text/html
and HTTP Status of 200.
We can override Content-Type
header and HTTP Status using content_type
and status
keyword arguments respectively. For example:
1 2 | def my_view(request):
return render(request,'myapp/markdown.md', context_dict, content_type=`text/markdown`)
|
This returns a HttpResponse
object with text/markdown
content type and default HTTP status of 200 OK.
1 2 | def my_view(request):
return render(request,'myapp/404.html', context_dict, status=404)
|
This returns a HttpResponse
object with text/html
content type and HTTP status of 404 Not Found.
1 2 | def my_view(request):
return render(request,'myapp/404.html', context_dict, content_type='application/json', status=405)
|
This returns a HttpResponse
object with application/json
content type and HTTP status of 404 METHOD NOT ALLOWED.
Load Comments