Understanding Http Redirections in Django
Tags: django, python, webdev, tutorial
Published: 2024-02-22 06:12:43

## Introduction
As a web developer, you may need to perform Http redirections in your backend logic. You may implement Http redirections under several circumstances:
* When redesigning a website URLs
* When rerouting traffic to HTTPS
* When enforcing authentication and authorization to protected resources
* When using canonical URLs
* When implementing a shortened URL functionality<br>

This article briefly discusses the above circumstances. Moreover, the article explains the Http redirection types you would typically perform. Finally, it elaborates on how you may implement temporary Http redirections in Django. The article provides a useful starting point for engineers getting started with Http redirections in Django. Additionally, the article could be an informative piece for general learning.

## How to Read this Article
The article has two sections, one that provides an overview of Http redirections in general, and the other that gets into Http redirections in Django.  You can [start from the beginning](#beginning) or [jump to implementing Http redirections in Django](#django).

<a id="beginning"></a>
## Why Redirect URLs?

![Http redirection process](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s8ic35vmgfn4zvrd7r9c.png)


### When Redesigning your Website URLs
As a web developer, you may perform major backend updates from time to time. The updates may involve changing the structure of your URLs or updating the URLs to new routes. In such a case, your website's users may encounter a [Page Not Found Error](https://en.wikipedia.org/wiki/HTTP_404#:~:text=In%20computer%20network%20communications%2C%20the,not%20find%20what%20was%20requested.) if they visit your old URLs. To prevent the error, you would let users make requests using the URLs they are used to, and then handle Http redirections to the new URLs.

### When Implementing a URL shortening functionality.
Sometimes you may want to convert long URL strings to shorter strings that are memorable. Implementing a URL shortening functionality may be necessary in this case, to enhance user experience. When users request resources from your server using the shortened URLs, they should be redirected to the actual URLs. In your backend logic, you would need to map the shortened URLs to their corresponding longer versions.

### When Re-routing Web Traffic to HTTPS
Many website applications reroute non-HTTPS traffic to HTTPS for security reasons. [HTTPS](https://en.wikipedia.org/wiki/HTTPS) is a secure protocol with which a web server and client computers communicate. System engineers typically configure web servers such as NGINX and Apache to redirect all non-HTTPS traffic.

![Http to Https](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g036wn5pa921k3sz8b0d.png)



### When Enforcing Authentication and Authorization
Websites restrict access to protected content by requiring prior user authentication and authorization. In such cases, if an unauthenticated or unauthorized user tries to access a protected resource, the website would redirect them to login or registration page. A backend engineer implements the Http redirection.

### When using Canonical URLs
A [canonical URL](https://www.semrush.com/blog/canonical-url-guide/) is the preferred URL(s) in a list of URLs that can access the same web resource. In some cases, web apps may have different URLs that map to the same resource. In such cases, web developers may implement canonical(read preferred URLs), for search engine optimization purposes. The idea is to map the least preferred URLs to the canonical URLs. In addition to optimizing search engines, canonicalization ensures predictable and consistent browsing experience for a website's users. Canonicalization can be implemented both on the front-end and back-end.

<a id="django"></a>
## Types of Redirection
In a Django Project, and based on the above Http redirection circumstances, a Django engineer would implement both temporary and permanent Http redirections. A temporary URL redirection means that, while a web resource can be internally accessed via a new URL, the client should continue using the old/original URL to request the resource. 

Suppose you are changing your URL formats but want the user to continue using previous URLs, which they are used to. You would implement a temporary redirection. When users visit your web resources using the previous URLs, your app would redirect them to the new URLs

You may also implement a permanent redirection. You use this redirection type where the structure of a URL has changed fundamentally. Permanent redirections instruct clients (browsers and search engine crawlers) to cache the new URL and use it to make future requests.

![Types of Http redirections](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/be5307bbbi0qd08sorp5.png)




HTTP status codes in the range [3XX](https://kinsta.com/knowledgebase/307-redirect/) represent the different types of Http redirections. The server returns status codes 302 and 307 for temporary redirections. In contrast, codes 301 and 308 signify permanent redirections. When running a Django web server in a development environment, you can inspect the server response header of a URL endpoint to check the Http redirection type.

## How to Implement a Temporary Redirection in Django
Here is how you would implement a temporary redirection in your Django project. You would typically implement the redirection in your views.py file. Suppose you need to redirect users to the home page after they have logged into their accounts. In this case, you would implement the Http redirection in your login view logic, after a user has been authenticated.
Assume you have this login view function:
```python
# views.py
from django.contrib.auth import authenticate, login
from django.http import HttpResponseRedirect
from django.urls import reverse

def login_user(request):
    """Logs in users into their accounts"""
    email = request.data.get('email')
    password = request.data.get('password')
    
    user = authenticate(request, email=email, password=password)
    if user is not none:
        login(request, user)
    return HttpResponseRedirect(reverse('myapp:index'))
```
In the above code, take note of the following lines:
```python
from django.http import HttpResponseRedirect
from django.urls import reverse

return HttpResponseRedirect(reverse('myapp:index'))
```

You would need to import the `HttpResponseRedirect` class object and the `reverse` function from the correct libraries as above. After you have defined the logic for your view function and need to perform a redirection, return HttpResponseRedirect class, passing the reverse function to it. Additionally, you need to pass the URL you want the view to redirect to as a parameter of the reverse function

### The HttpResponseRedirect Class
This is a Python class object used to perform Http redirections to specified URLs. The class takes a single parameter, the URL to redirect to. Below is the class's definition:
```python
class HttpResponseRedirect(HttpResponse):
    def __init__(self, redirect_to, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.['Location'] = iri_to_uri(redirect_to)
        self.status_code = 302
```
The `redirect_to` parameter represents the URL the client should be redirected to. This URL is set in the `Location header` of the server response.

### The reverse() Function
The reverse function dynamically generates a URL string based on a view's namespace or its URL pattern. The namespace is defined in a Django app's urls.py configuration file. Below is the reverse function's signature:
```python
def reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None):
```
The `viewname` parameter is the main argument. This parameter is required. `viewname` is the namespace of a view function or the associated URL pattern specified in your app's url.py file. Returning to our login example above, in a Django app called 'app', you might have configured your index page URL configuration like this:
```python
# urls.py
url_patterns = [
    path('/index/', views.index, name='index'),
                                ...
]
```
`index` is the namespace for the index view function, or namespace for your index URL pattern. A namespace can be understood as an identifier for a particular view or URL. You can reference a URL or a view anywhere in your Django app using its namespace.

Other parameters passed to reverse are optional. 
  
* `urlconf`: allows you to specify a URL pattern for the redirect URL.  When building a Django project, you define several patterns in different apps for your URLs. By default, the root URL pattern for the current app is selected. 
* `args`: allows you to pass positional arguments to reverse as a list or tuple.
* `kwargs`: a dictionary containing keyword arguments you may need to pass into reverse. You cannot pass args and kwargs at the same time
* `current_app`: Specifies the name of the app that the namespace belongs to.

The reverse function constructs and returns a URL string when the resolver finds a URL pattern matching the namespace. If no match is found, the resolver raises a [NoReverseMatch](https://docs.djangoproject.com/en/dev/ref/exceptions/#django.urls.NoReverseMatch) exception.

### Use Cases
In some cases, you may pass a URL string as an argument directly to the HttpResponseRedirect class instance. In this case, you are hardcoding the redirection. In case you change the structure of your redirect URL, you would need to explicitly update the URL string passed to HttpResponseRedirect.
```python
# views.py
from django.http import HttpResponseRedirect

def my_view(request):
    # Assuming redirect_url is the URL you want to redirect to
    # Redirect URL could be a relative or absolute path of a URL
    # within your Django project
    redirect_url = "/some/redirect/url"
    return HttpResponseRedirect(redirect_url) 
```

However, it is considered best practice to use HttpResponseRedirect with reverse. The technique offers more flexibility and makes it easier to maintain and read code.

Take this use case for example:
```python
# views.py
from django.http import HttpResponseRedirect
from django.urls import reverse

def my_view(request):
    # Some logic to determine view name or URL pattern dynamically
    if some_condition:
        view_name = 'app_name:view_name_1'
    else:
        view_name = 'app_name:view_name_2'

    url = reverse(view_name)
    return HttpResponseRedirect(url)
```
In the above case, if you change the structure of a URL, you only need to update the value of `view_name` variables. Moreover, you can dynamically redirect to different URLs based on custom conditions. The approach offers more freedom.

### A Complex Case
As discussed above, the reverse function takes other optional parameters. Sometimes you may need to redirect to a URL that takes additional parameters in the URL string. In this situation, you need to pass the additional parameters either as positional or keyword arguments to reverse.

Assume you are writing a view that updates the information about an object stored in a database. The view retrieves the object by ID and should redirect to a URL that returns the updated object. In such a case, you may pass the object's ID as a keyword argument to reverse. Look at the example below:
```python
# views.py
from django.http import HttpResponseRedirect
from django.urls import reverse

def update_object(request, pk):
    """
    Updates an object by id
    args:
        request: request object
        pk: object id
    """
                ...
    return HttpResponseRedirect(reverse('myapp:some_view_name', 
        kwargs={'pk': pk}))
```
## Conclusion
A Django engineer may occasionally need to perform Http redirections for various reasons. Understanding the various redirection types and implementation strategies is key to executing successful redirections. Python provides tools that facilitate Http redirections. The HttpResponseRedirect class object takes in a URL string and redirects the current view to the redirect URL, which is the URL string argument passed to the class instance. When the class object is used with the reverse function, Http redirection becomes more flexible. The reverse function takes in a namespace, representing a view or the view's URL pattern. Then a URL resolver engine matches the namespace to a configured URL pattern and executes the redirection. That's all. Have fun implementing Http redirections in your Django project!