Creating URLs and Custom Response

Reversing URL Patterns

The process of creating a URL from the name of the URL pattern is called reversing a URL. So why we do that? Why not just hardcode URLs?

Because at a later date, If you want to update the URL structure you would have to manually visit each Python module and template to make the changes. On the other hand, if you have created URLs by reversing URL patterns, all you need is to update the URL pattern in the urls.py file. Templates and modules will pick the changes automatically.

reverse() Function

To reverse a URL we use the reverse() function. It accepts the name of the URL pattern and returns URL without the host portion. When reverse() can’t reverse a URL It raises NoReverseMatch exception. To use this function we first have to import it from django.shortcuts module.

To see reverse() method in action open interactive Python shell by issuing the following command.

Noticed something different here? Well, here we are starting Python shell using the ./manage.py shell instead of just python. But why? Because in addition to invoking Python interpreter ./manage.py shell imports bare minimum of Django to work with. We will refer to this shell as Django shell.

Currently, our djangobin app consists of following 6 URL patterns:

djangobin/django_project/djangobin/urls.py*

Let’s try reversing each URL pattern one by one in the Django shell.

We have successfully reversed 4 URLs. What about the remaining two URL patterns?

The remaining two URL patterns have one or more dynamic components in it, as a result, we are required to pass these components to successfully reverse them. Trying to call reverse() on such URL patterns without passing dynamic components will throw a NoReverseMatch exception.

Also note that there are two URL patterns of the same name, book_category, the first one has no dynamic component but the second one has one dynamic component. When we called reverse("book_category") in the preceding example Django was smart enough to infer that we want to reverse URL without the dynamic component.

To reverse dynamic URLs pattern pass dynamic components to args keyword argument as a list.

Here are some examples:

We also have the option to pass dynamic components to reverse() as a dictionary:

Here are some examples:

When using kwargs keep in mind that the name of the key must be same as that of the named group in the URL pattern. Otherwise, you will get NoReverseMatch exception.

Custom Response

By default, HttpResponse objects gets created with Content-Type:text/html header and HTTP status code of 200. We can change content type header and status code using content_type and status keyword arguments, respectively.

This will send the response to the client using content type text/plain. In other words, the client (browser) would render the text <p>Custom response 1</p> literally, instead of interpreting it as HTML.

When building API, you will need to send data as JSON. This can be easily done by setting the content_type header to application/json. For example:

And here is how we can change the default status code:

This view function will return HTTP 404 Not Found Error with content <h1>HTTP 404 Not Found</h1>.

We can also set additional headers by treating HttpResponse instance as a dictionary.

This view function will redirect the user to http://example.com/ using a temporary redirect (HTTP 302 redirect).

Here is another example:

This view function will force the browser to treat the response as a file attachment. We will use the exact same technique to download the code snippets.

Common subclasses of HttpResponse

The following table lists some common subclasses of HttpResponse:

ClassName Description
HttpResponseRedirect It takes path to redirect to as a first argument and perform a temporary redirect (HTTP status code 302). The path can fully qualified (like http://example.com/contact), absolute (/books/crime/) or relative (search/).
HttpResponsePermanentRedirect Same as HttpResponseRedirect but performs a permanent redirect (HTTP status code 301)
HttpResponseNotFound Same as HttpResponse but returns a status code of 404.
HttpResponseForbidden Same as HttpResponse but returns a status code of 403.
HttpResponseBadRequest Same as HttpResponse but returns a status code of 400.
HttpResponseServerError Same as HttpResponse but returns a status code of 500.

All these classes live in django.http package. But you can also import
HttpResponseRedirect and HttpResponsePermanentRedirect classes from django.shortcuts module.

The following listing demonstrates how to use these classes:

Built-in Shortcuts for triggering HTTP Errors

Django also provides some shortcuts for triggering HTTP errors explicitly from your view function. The following table lists some exceptions and the HTTP errors they trigger:

Exception HTTP Status Code
django.http.Http404 or django.shortcuts.Http404 404 Not Found
django.core.exceptions.SuspiciousOperation 400 Bad Request
django.core.exceptions.PermissionDenied 403 Forbidden
Exception 500 Internal Server Error

The following listing shows how to use these exceptions:

When DEBUG is True all these exceptions will show a detailed page describing the nature of the problem. For example, Http404 exception will generate a page like this:

And SuspiciousOperation exception will generate a page like this:

Once we go in Production (DEBUG is False) they all will return a generic error page, just containing the name of the error without any sensitive details. For example, Http404 exception will generate a page like this:

And PermissionDenied exception will generate a page like this:

redirect() shortcut

In the previous sections, we have seen several ways to perform temporary (HTTP status code 302) and permanent redirects (HTTP status code 301). The redirect() is a shortcut function to redirect the users to different URLs. It accepts URL path or name of the URL pattern or fully qualified URL to redirect to. To use it first import it from django.shortcuts module.

The following view function will redirect the user to http://example.com.

By default, the redirect() performs a temporary redirect (HTTP status code 302), to perform a permanent redirect pass permanent=True. For example:

If the URL accepts arguments, pass them to redirect() after the name of the URL pattern. For example:

This view function will redirect the user to /user/foobar/ using a permanent redirect.

Namespacing URLs

As your project grows, you will have a hard time preventing name clashes between your URL patterns. For example, let’s say we have two apps, a forum and a blog. Both have a URL pattern named index in their urls.py file. So What would happen if you try to reverse index URL pattern using the reverse() function?

Django will generate the URL based upon the order in the which the app URLConf is registered in the sitewide urls.py file. The URLConf which come at the end of the list will override the URL pattern in the of the same name in the URLConf above it.

We can solve this issue by adding a namespace to our URL patterns. Namespacing URLs allows us to refer to a group of URLs using a unique identifier.

There are two ways to add the namespace:

  1. app_name attribute .
  2. include() function.

Let’s explore both methods:

Using app_name

Open urls.py from djangobin app and define app_name variable just above urlpatterns list as follows:

djangobin/django_project/djangobin/urls.py

From now on, to reverse URLs you have to precede the name of the URL pattern with djangobin followed by a colon (:).

The following shell session demonstrates reversing of namespaced URL patterns:

Trying to reverse URLs without specifying the namespace will result in NoReverseMatch exception:

Using include() function

Recall that include() function is used to include a URLConf from an application under the specified path.

Another way to create namespace is to define it while including a URLConf. Open sitewide urls.py file and modify include() to include an extra keyword argument named namespace as follows:

djangobin/django_project/django_project/urls.py

Throughout this tutorial, we will use the second method to namespace URLs, so comment out the line where we have defined the namespace using the app_name variable in djangobin’s urls.py file.

That’s all is needed to create namespaced URLs.

Leave a Comment