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 HTTP Status codes 404 and 500, 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.

  1. https://docs.djangoproject.com/en/1.11/ref/request-response/#django.http.HttpResponse