View method responses
The render()
method
to generate view method responses you've used up to this point is
actually a shortcut. You can see toward the top of listing 2-20,
the render()
method is part of the
django.shortcuts
package.
This means there are other
alternatives to the render()
method to generate a view
response, albeit the render()
method is the most
common technique. For starters, there are three similar variations
to generate view method responses with data backed by a template,
as illustrated in listing 2-21.
Listing 2-21 Django view method response alternatives
# Option 1) from django.shortcuts import render def detail(request,store_id='1',location=None): ... return render(request,'stores/detail.html', values_for_template) # Option 2) from django.template.response import TemplateResponse def detail(request,store_id='1',location=None): ... return TemplateResponse(request, 'stores/detail.html', values_for_template) # Option 3) from django.http import HttpResponse from django.template import loader, Context def detail(request,store_id='1',location=None): ... response = HttpResponse() t = loader.get_template('stores/detail.html') c = Context(values_for_template) return response.write(t.render(c))
The first option in listing 2-21
is the django.shortcuts.render()
method which shows
three arguments to generate a response: the (required)
request
reference, a (required) template route and an
(optional) dictionary -- also known as the context -- with data to
pass to the template.
There are three more (optional)
arguments for the render()
method which are not shown
in listing 2-21: content_type
which sets the HTTP
Content-Type
header for the response and which
defaults to DEFAULT_CONTENT_TYPE
parameter in
settings.py
, which in itself defaults to
text/html
; status
which sets the HTTP
Status
code for the response which defaults to
200
; and using
to specify the template
engine -- either jinja2
or django
-- to
generate the response. The next section on HTTP handling for the
render()
method describes how to use
content_type
& status
, while chapters
3 & 4 talk about Django templates and Jinja templates.
The second option in listing 2-21
is the django.template.response.TemplateResponse()
class, which in terms of input is nearly identical to the
render()
method. The difference between the two
variations is, TemplateResponse()
can alter a response
once a view method is finished (e.g. via middleware), where as the
render()
method is considered the last step in the
lifecycle after a view method finishes. You should use
TemplateResponse()
when you foresee the need to modify
view method responses in multiple view methods after they finish
their work, a technique that's discussed in a later section in this
chapter on view method middleware.
There are four more (optional)
arguments for the TemplateResponse()
class which are
not shown in listing 2-21: content_type
which defaults
to text/html
; status
which defaults to
200
; charset
which sets the response
encoding from the HTTP Content-Type
header or
DEFAULT_CHARSET
in settings.py
which in
itself defaults to utf-8
; and using
to
indicate the template engine -- either jinja2
or
django
-- to generate the response.
The third option in listing 2-21
represents the longest, albeit the most flexible response creation
process. This process first creates a raw HTTPResponse
instance, then loads a template with the
django.template.loader.get_template()
method, creates
a Context()
class to load values into the template,
and finally writes a rendered template with its context to the
HTTPResponse
instance. Although this is longest of the
three options, it's the preferred choice when a view method
response requires advanced options. The upcoming section on
built-in response shortcuts for in-line & streamed content, has
more details on HTTPResponse
response types.
Response options for HTTP Status and Content-type headers
Browsers set HTTP headers in
requests to tell applications to take into account certain
characteristics for processing. Similarly, applications set HTTP
headers in responses to tell browsers to take into account certain
characteristics for the content being sent out. Among the most
important HTTP headers set by applications like Django are
Status
and Content-Type
.
The HTTP Status
header is a three digit code number to indicate the response status
for a given request. Examples of Status
values are
200
which is the standard response for successful HTTP
requests and 404
which is used to indicate a requested
resource could not be found. The HTTP Content-Type
header is a MIME(Multipurpose Internet Mail Extensions) type string
to indicate the type of content in a response. Examples of
Content-Type
values are text/html
which
is the standard for an HTML content response and
image/gif
which is used to indicate a response is a
GIF image.
By default and unless there's an
error, all Django view methods that create a response with
django.shortcuts.render()
, a
TemplateResponse()
class or
HttpResponse()
class -- illustrated in listing 2-21 --
create a response with the HTTP Status
value set to
200
and the HTTP Content-Type
set to
text/html
. Although these default values are the most
common, if you want to send a different kind of response (e.g. an
error or non-HTML content) it's necessary to alter these
values.
Overriding HTTP
Status
and Content-Type
header values for
any of the three options in listing 2-21 is as simple as providing
the additional arguments status
and/or
content_type
. Listing 2-22 illustrates various
examples of this process.
Listing 2-22 HTTP Content-type and HTTP Status for Django view method responses
from django.shortcuts import render # No method body(s) and only render() example provided for simplicity # Returns content type text/plain, with default HTTP 200 return render(request,'stores/menu.csv', values_for_template, content_type='text/plain') # Returns HTTP 404, wtih default text/html # NOTE: Django has a built-in shortcut & template 404 response, described in the next section return render(request,'custom/notfound.html',status=404) # Returns HTTP 500, wtih default text/html # NOTE: Django has a built-in shortcut & template 500 response, described in the next section return render(request,'custom/internalerror.html',status=500) # Returns content type application/json, with default HTTP 200 # NOTE: Django has a built-in shortcut JSON response, described in the next section return render(request,'stores/menu.json', values_for_template, content_type='application/json')
The first example in listing 2-22
is designed to return a response with plain text content. Notice
the render
method content_type
argument.
The second and third examples in listing 2-22 set the HTTP
Status
code to 404
and 500
.
Because the HTTP Status 404
code is used for resources
that are not found, the render
method uses a special
template for this purpose. Similarly, because the HTTP
Status
500
code is used to indicate an
error, the render
method also uses a special template
for this purpose.
The fourth and last example in
listing 2-22 is designed to return a response with JavaScript
Object Notation(JSON) content. The HTTP Content-Type
application/json
is a common requirement for requests
made by browsers that consume JavaScript data via Asynchronous
JavaScript (AJAX).
Tip Django has built-in shortcuts and templates to deal with HTTPStatus
codes404
and500
, as well as a JSON short-cut response, all of which are described in the next section and that you can use instead of the examples in listing 2-22.
Built-in response shortcuts and templates for common HTTP Status: 404 (Not Found), 500 (Internal Server Error), 400 (Bad Request) and 403 (Forbidden)
Although Django automatically
triggers an HTTP 404 Status
(Not Found) response when
a page is not found and also triggers an HTTP 500
Status
(Internal Server Error) response when an
unhandled exception is thrown in a view. Django has built-in
shortcuts and templates that are meant to be used explicitly in
Django views when you know end users should get them. Table 2-3
illustrates the different shortcuts to trigger certain HTTP status
responses.
Table 2-3 Django shortcut exceptions to trigger HTTP statuses
HTTP status code | Python code sample |
---|---|
404 (Not Found) | from django.http import Http404 raise Http404 |
500 (Internal Server Error) | raise Exception |
400 (Bad Request) | from django.core.exceptions import SuspiciousOperation raise SuspiciousOperation |
403 (Forbidden) | from django.core.exceptions import PermissionDenied raise PermissionDenied |
*Django automatically handles not found pages raising HTTP 404 and unhandled exceptions raising HTTP 500
As you can see in the examples in
table 2-3, the shortcut syntax is straightforward. For example, you
can make evaluations in a Django view like if article_id <
100:
or if unpayed_subscription:
and based on
the result throw exceptions from table 2-3 so end users get the
proper HTTP status response.
So what is the actual content
sent in a response besides the HTTP status when an exception from
table 2-3 is triggered ? The default for HTTP 400
(Bad
Request) and HTTP 403
(Forbidden) is a single line
HTML page that says "Bad Request (400)
" and "403
Forbidden
", respectively. For HTTP 404
(Not
Found) and HTTP 500
(Internal Server Error), it
depends on the DEBUG
value in
settings.py
.
If a Django project has
DEBUG=True
in settings.py
, HTTP
404
(Not Found) generates a page with the available
urls -- as illustrated in figure 2-1 -- and HTTP 500
(Internal Server Error) generates a page with the detailed error --
as illustrated in figure 2-2. If a Django project has
DEBUG=False
in settings.py
, HTTP
404
(Not Found) generates a single line HTML page that
says "Not Found. The requested URL <url_location> was
not found on this server.
" and HTTP 500
(Internal Server Error) generates a single line HTML page that says
"A server error occurred. Please contact the
administrator.
".
Figure 2-1. HTTP 404 for Django project when DEBUG=True
Figure 2.2- HTTP 500 for Django project when DEBUG=True
It's also possible to override
the default response page for all the previous HTTP codes with
custom templates. To use a custom response page, you need to create
a template with the desired HTTP code and .html
extension. For example, for HTTP 403
you would create
the 403.html
template and for HTTP 500
you would create the 500.html
template. All these
custom HTTP response templates need to be placed in a folder
defined in the DIRS
list of the TEMPLATES
variable so Django finds them before it uses the default HTTP
response templates.
Caution Custom 404.html and 500.html pages only work when DEBUG=False
If DEBUG=True
, it
doesn't matter if you have 404.html
or
500.html
templates in the right location, Django uses
the default response behavior illustrated in figure 2-1 and figure
2-2, respectively. You need to set DEBUG=False
for the
custom 404.html
and 500.html
templates to
work.
On certain occasions, using
custom HTTP response templates may not be enough. For example, if
you want to add context data to a custom template that handles an
HTTP response, you need to customize the built-in Django HTTP view
methods themselves, because there's no other way to pass data into
this type of template. To customize the built-in Django HTTP view
methods you need to declare special handlers in a project's
urls.py
file. Listing 2-23 illustrates the
urls.py
file with custom handlers for Django's
built-in HTTP Status
view methods.
Listing 2-23. Override built-in Django HTTP Status view methods in urls.py
# Overrides the default 400 handler django.views.defaults.bad_request handler400 = 'coffeehouse.utils.views.bad_request' # Overrides the default 403 handler django.views.defaults.permission_denied handler403 = 'coffeehouse.utils.views.permission_denied' # Overrides the default 404 handler django.views.defaults.page_not_found handler404 = 'coffeehouse.utils.views.page_not_found' # Overrides the default 500 handler django.views.defaults.server_error handler500 = 'coffeehouse.utils.views.server_error' urlpatterns = [.... ]
Caution If DEBUG=True, the handler404 and handler500 handlers won't work, Django keeps using the built-in Django HTTP view methods. You need to set DEBUG=False for the handler404 and handler500 handlers to work.
As you can see in listing 2-23,
there are a series of variables in urls.py
right above
the standard urlpatterns
variable. Each variable in
listing 2-23 represents an HTTP Status
handler, with
its value corresponding to a custom Django view to process
requests. For example, handler400
indicates that all
HTTP 400
requests should be handled by the Django view
method coffeehouse.utils.views.bad_request
instead of
the default django.views.defaults.bad_request
. The
same approach is taken for HTTP 403
requests using
handler403
, HTTP 404
requests using
handler404
and HTTP 500 requests using
handler500
.
As far as the actual structure of custom Django view methods is concerned, they are identical to any other Django view method. Listing 2-24 shows the structure of the custom view methods used in listing 2-23.
Listing 2-23. Custom views to override built-in Django HTTP view methods
from django.shortcuts import render def page_not_found(request): # Dict to pass to template, data could come from DB query values_for_template = {} return render(request,'404.html',values_for_template,status=404) def server_error(request): # Dict to pass to template, data could come from DB query values_for_template = {} return render(request,'500.html',values_for_template,status=500) def bad_request(request): # Dict to pass to template, data could come from DB query values_for_template = {} return render(request,'400.html',values_for_template,status=400) def permission_denied(request): # Dict to pass to template, data could come from DB query values_for_template = {} return render(request,'403.html',values_for_template,status=403)
As you can see in listing 2-24,
the custom HTTP view methods use the same render
method from django.shortcuts
as previous view method
examples. The methods point to a template named by the HTTP
Status
code, use a custom data dictionary that becomes
accessible on the template and use the status
argument
to indicate the HTTP status code.
Built-in response shortcuts for in-line & streamed content
All the prior view response examples have worked on the basis of content being structured through a template. However, there can be times when using a template to output a response is unnecessary (e.g. a one line response that says "Nothing to see here").
Other times it makes no sense for
a response to use a template, such is the case for HTTP
301
(Permanent Redirect) or HTTP 302
(Redirect) where the response just requires a redirection url.
Table 2-4 illustrates the different shortcuts to trigger HTTP
redirects.
Table 2-4 Django shortcuts for HTTP redirects
HTTP status code | Python code sample |
---|---|
301 (Permanent Redirect) | from django.http import HttpResponsePermanentRedirect return HttpResponsePermanentRedirect("/") |
302 (Redirect) | from django.http import HttpResponseRedirect return HttpResponseRedirect("/") |
Both samples in table 2-4
redirect to an application's home page (i.e."/"
).
However, you can also set the redirection to any application url or
even a full url on a different domain
(e.g.http://maps.google.com/
).
In addition to response redirection shortcuts, Django also offers a series of response shortcuts where you can add in-line responses. Table 2-5 illustrates the various other shortcuts for HTTP status codes with in-line content responses.
Table 2-5 Django shortcuts for in-line and streaming content responses
Purpose or HTTP Status code | Python code sample |
304 (NOT MODIFIED) | from django.http import HttpResponseNotModified return HttpResponseNotModified()* |
400 (BAD REQUEST) | from django.http import HttpResponseBadRequest return HttpResponseBadRequest("<h4>The request doesn't look right</h4>") |
404 (NOT FOUND) | from django.http import HttpResponseNotFound return HttpResponseNotFound("<h4>Ups, we can't find that page</h4>") |
403 (FORBIDDEN) | from django.http import HttpResponseForbidden return HttpResponseForbidden("Can't look at anything here",content_type="text/plain") |
405 (METHOD NOT ALLOWED) | from django.http import HttpResponseNotAllowed return HttpResponseNotAllowed("<h4>Method not allowed</h4>") |
410 (GONE) | from django.http import HttpResponseGone return HttpResponseGone("No longer here",content_type="text/plain") |
500 (INTERNAL SERVER ERROR) | from django.http import HttpResponseServerError return HttpResponseServerError("<h4>Ups, that's a mistake on our part, sorry!</h4>") |
In-line response that serializes data to JSON (Defaults to HTTP 200 and content type application/json) | from django.http import JsonResponse data_dict = {'name':'Downtown','address':'Main #385','city':'San Diego','state':'CA'} return JsonResponse(data_dict) |
In-line response that stream data (Defaults to HTTP 200 and streaming content which is an iterator of strings) | from django.http import StreamingHttpResponse return StreamingHttpResponse(large_data_structure) |
In-line response that stream binary files (Defaults to HTTP 200 and streaming content) | from django.http import FileResponse return FileResponse(open('Report.pdf','rb')) |
In-line response with any HTTP status code (Defaults to HTTP 200) | from django.http import HttpResponse return HttpResponse("<h4>Django in-line response</h4>") |
* The HTTP 304 status code indicates a 'Not Modified' response, so you can't send content in the response, it should always be empty.
As you can see in the samples in
table 2-5, there are multiple shortcuts to generate different HTTP
Status responses with in-line content and entirely forgo the need
to use a template. In addition, you can see the shortcuts in table
2-5 can also accept the content_type
argument if the
content is something other than HTML (i.e.
content_type=text/html
).
Since non-HTML responses have
become quite common in web applications, you can see table 2-5 also
shows three Django built-in response shortcuts to output non-HTML
content. The JsonResponse
class is used to transform
an in-line response into JavaScript Object Notation (JSON). Because
this response converts the payload to a JSON data structure, it
automatically sets the content type to
application/json
. The
StreamingHttpResponse
class is designed to stream a
response without the need to have the entire payload in-memory, a
scenario that's helpful for large payload responses. The
FileResponse
class -- a subclass of
StreamingHttpResponse
-- is designed to stream binary
data (e.g. PDF or image files).
This takes us to the last entry
in table 2-5, the HttpResponse
class. As it turns out,
all the shortcuts in table 2-5 are customized subclasses of the
HttpResponse
class, which I initially described in
listing 2-21 as one of the most flexible techniques to create view
responses.
The HttpResponse
method is helpful to create responses for HTTP status codes that
don't have direct shortcut methods (e.g. HTTP 408
[Request Timeout], HTTP 429
[Too Many Requests]) or to
inclusively harness a template to generate in-line responses as
illustrated in listing 2-24.
Listing 2-24. HttpResponse with template and custom CSV file download
from django.http import HttpResponse from django.utils import timezone from django.template import loader, Context response = HttpResponse(content_type='text/csv') response['Content-Disposition'] = 'attachment; filename=Users_%s.csv' % str(timezone.now().today()) t = loader.get_template('dashboard/users_csvexport.html') c = Context({'users': sorted_users,}) response.write(t.render(c)) return response
The HTTPResponse
object in listing 2-24 is generated with a text/csv
content type to advise the requesting party (e.g. browser) that
it's about to receive CSV content. Next, the
Content-Disposition
header also tells the requesting
party (e.g. browser) to attempt to download the content as a file
named Users_%s.csv
where the %s
is
substituted with the current server date.
Next, using the
loader
module we use the get_template
method to load the template users_csvexport.html
that
will have a CSV like structure with data placeholders. Then we
create a Context
object to hold the data that will
fill the template, which in this case it's just a single variable
named users
. Next, we call the template's
render
method with the context
object in
order to fill in the template's data placeholders with the data.
Finally, the rendered template is written to the
response
object via the write
method and
the response
object is returned.
The HttpResponse
class offers over twenty options between attributes and
methods[5], in addition to the
content_type
and status
parameters.