Creating URLs in Django

Up until now, we have been hardcoding URLs in our templates. At a later date, If we want to update the URL structure we would have to manually visit each and every template. This problem can be solved easily by using the url tag in the templates.

The url tag

The url tag helps us to generate links in the templates. It has the following syntax:

Syntax: {% url 'url_name' arg1 arg2 %}

where url_name is the value we passed to the name keyword argument of the url() function. The arg1 and arg1 are additional arguments required by the view function. On success, it returns part of the URL without host portion. If it can’t create URL NoReverseMatch exception is thrown.

Currently, urls.py file in the blog app looks like this:

TGDB/django_project/blog/urls.py

The following code will create the URL for the post_list URL pattern.

Notice that we are not passing any arguments to the url tag because the post_list URL pattern doesn’t accept any. In other words, post_list() view function doesn’t accept any additional arguments apart from the request argument.

Inside the template, the above code output would output '/'. We can verify this in the Django Shell.

In case the specified URL pattern doesn’t exist, then the url tag throws a NoReverseMatch exception.

The reverse() function

What if a need arises to generate URLs in the in the Python code, for example in a view function? To create URLs in Python code we use the reverse() function. It accepts the name of the URL pattern. To use this method we first have to import it from django.urls. Just like the url tag on success it returns part of the URL without the host, otherwise, a NoReverseMatch exception is thrown.

Passing arguments to the url tag and reverse() function

Consider the following URL pattern:

At first, you might think, just as before, we can easily create URL for this pattern in our template like this:

But you would be wrong. In this case (?P<category>[\w-]+) part of the regular expression is unknown, so we have to pass one additional parameter to the url tag to generate the complete URL.

We can also pass additional arguments as variables like this:

Notice that there are no quotation marks around cat.

If URL pattern requires more than one arguments, then separate each argument by a space character.

Similarly, In case the URL pattern needs, we can pass additional arguments to the reverse() function as follows:

Here is an example.

We can also pass additional argument as keyword arguments like this:

Notice that the name of the key in the kwargs dictionary should match the named group in the URL pattern. Otherwise, you would get NoReverseMatch exception.

We now know how to create URLs. Let’s update post_list.html, post_detail.html, post_by_category.html and post_by_tag.html templates to use the url tags as follows (changes are highlighted).

TGDB/django_project/blog/templates/blog/post_list.html

TGDB/django_project/blog/templates/blog/post_detail.html

TGDB/django_project/blog/templates/blog/post_by_category.html

TGDB/django_project/blog/templates/blog/post_by_tag.html

Eliminating Redundancy

If you pay close attention to the templates we just modified you will find that all of the four templates share the same p.post-info paragraph. The problem is that if we add or update something inside p.post-info paragraph then we would have to revisit every template to apply the changes, which is very bad. Instead, It would be much better to create a separate template for the contents of p.posts-info and then include this template in the each of our existing templates, this way we only have to update the contents of p.post-info paragraph at one place and all other templates will pick the changes automatically.

Create a new template called post_info.html and add the following code to it.

TGDB/django_project/blog/templates/blog/post_info.html

Then include this template in all other templates using the include tag as follows:

TGDB/django_project/blog/templates/blog/post_list.html

TGDB/django_project/blog/templates/blog/post_detail.html

TGDB/django_project/blog/templates/blog/post_by_category.html

TGDB/django_project/blog/templates/blog/post_by_tag.html

The get_absolute_url() method

The get_absolute_url() is just another method commonly employed by Django developers to generate URLs. Unlike the url tag and reverse() function, we can use get_absolute_url() in our Python code as well as in templates. To use this method you must first implement it in the model’s class.

Let’s define it in the Category model first, just below the __str__() method, as follows:

TGDB/django_project/blog/models.py

Instead of manually creating URL you could use reverse() method like this:

Calling get_absolute_url() in Python code.

Make sure you restart Django Shell before typing the following code.

Calling get_absolute_url() in templates.

It may seem like get_absolute_url() method is functionally equivalent to the url tag and the reverse() method, but this is not entirely true. The get_absolute_url() method has the following advantages:

  1. Django documentation advocates that you should use get_absolute_url() in templates. The reason is that, at a later date, if we want to change the structure of the URL, then we just need to modify get_absolute_url() and all the templates will pick up the changes automatically. It also means that you don’t have to remember whether Category or Tag object takes id or any other argument.
  2.  The redirect() method which we will discuss in Redirecting URLs in Django also uses the URL returned by get_absolute_url() method to perform a temporary redirect.
  3. Various third-party libraries available at PyPI also uses get_absolute_url() for different tasks.
  4. If get_absolute_url() method is defined in the model class, then Django Admin site uses it in the object editing page to create a "VIEW ON SITE" link. So what the link does? This link will take you to the public view of the object. For example, let’s say you are editing a category named python then the "VIEW ON SITE" link will take you to the Category page which displays all post published under python category.

Too see "VIEW ON SITE" link in action, login to Django Admin and visit Change category page. You will see a link "VIEW ON SITE" in the upper right corner of the page. Click the link and Django Admin will redirect you to the category page for that category.

While we are at it. Let’s define get_absolute_url() method for Post and Tag models.

TGDB/django_project/blog/models.py

Let’s update our templates to use get_absolute_url() method instead of url tag.

TGDB/django_project/blog/templates/blog/post_list.html

TGDB/django_project/blog/templates/blog/post_by_category.html

TGDB/django_project/blog/templates/blog/post_by_tag.html

TGDB/django_project/blog/templates/blog/post_info.html

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

Leave a Comment