Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django's redirects app doesn't work with URL parameters

I recently installed Django's default redirects app on my site using the exact instructions specified:

  1. Ensured django.contrib.sites framework is installed.
  2. Added 'django.contrib.redirects' to INSTALLED_APPS.
  3. Added 'django.contrib.redirects.middleware.RedirectFallbackMiddleware' to MIDDLEWARE.
  4. Migrated database using manage.py migrate.

The app works great when my URL is clean, like so:

Redirect setting: /page/ to /redirect/

When I visit https://www.example.com/page/ => redirects to https://www.example.com/redirect/

However, the redirects don't work when URL parameters are appended to the end of my URL, like so:

Redirect setting: /page/ to /redirect/

When I visit https://www.example.com/page/?url_parameter=true => throws Http404 error (page not found)

Ideally, I'd like my redirects to preserve URL parameters, like so:

Redirect setting: /page/ to /redirect/

When I visit https://www.example.com/page/?url_parameter=true => redirects to https://www.example.com/redirect/?url_parameter=true

Is there a way to force the redirects app to preserve URL parameters per the above?

like image 548
Sam Avatar asked Apr 03 '17 19:04

Sam


People also ask

Can we pass parameter in redirect in Django?

Django Redirects: A Super Simple Example To avoid hard-coding the URL, you can call redirect() with the name of a view or URL pattern or a model to avoid hard-coding the redirect URL. You can also create a permanent redirect by passing the keyword argument permanent=True .

Why is permanent redirection not a good option Django?

Using permanent redirects might introduce problems (think about podcast players on your phone that remember such redirects and won't recognize it when you move your static file server).

How do I redirect a URL to a view?

You can pass positional or keyword argument(s) to the redirect shortcut using the reverse() method and the named url of the view you're redirecting to.

How do I redirect a URL in Python?

request module. Define a web page URL, suppose this URL will be redirected when you send a request to it. Get the response object. Get the webserver returned response status code, if the code is 301 then it means the URL has been redirected permanently.


1 Answers

1.You need to write your own CustomRedirectFallbackMiddleware by subclass django.contrib.redirects.middleware.RedirectFallbackMiddleware and override process_response method of django.contrib.redirects.middleware.RedirectFallbackMiddleware.

custom_redirect_middleware.py

import urlparse

from django import http
from django.conf import settings
from django.contrib.redirects.models import Redirect
from django.contrib.sites.shortcuts import get_current_site
from django.contrib.redirects.middleware import RedirectFallbackMiddleware


class CustomRedirectFallbackMiddleware(RedirectFallbackMiddleware):
    response_gone_class = http.HttpResponseGone
    response_redirect_class = http.HttpResponsePermanentRedirect

    def process_response(self, request, response):
        if response.status_code != 404:
            return response

        full_path = request.get_full_path() 
        """ 
            Seperate query parameters and url if full absolute path contains 
            query parameters using python urlparse library       
        """
        parsed_url = None
        if "?" in full_path:
            parsed_url = urlparse.urlparse(full_path)
            # Now full path contains no query parameters
            full_path = parsed_url.path

        current_site = get_current_site(request)

        r = None
        try:
            r = Redirect.objects.get(site=current_site, old_path=full_path)
        except Redirect.DoesNotExist:
            pass
        if r is None and settings.APPEND_SLASH and not request.path.endswith('/'):
            try:
                if parsed_url is not None:
                    r = Redirect.objects.get(
                        site=current_site,
                        old_path= full_path + '/',
                        )
                else:
                    r = Redirect.objects.get(
                        site=current_site,
                        old_path=request.get_full_path(force_append_slash=True),
                        )
            except Redirect.DoesNotExist:
                pass

        if r is not None:
            if r.new_path == '':
                return self.response_gone_class()

            #Adding back the query parameters to redirecting path
            if parsed_url is not None:
                new_path_with_query_params = r.new_path + "?" + parsed_url.query
                return self.response_redirect_class(new_path_with_query_params)

            #Handles redirections for urls without query parameters
            return self.response_redirect_class(r.new_path)

        return response

In this CustomRedirectFallbackMiddleware

i. Simply separated URL parameters from full_absolute_path.

ii. Next got exact redirect_path of separated full_absolute_path form django.contrib.redirects database.

iii. Then appended the URL parameters which separated from full_absolute_path is to redirect_path.

2.Remove 'django.contrib.redirects.middleware.RedirectFallbackMiddleware' from your settings.py and add below line to MIDDLEWARE of settings.py.

# Here base is my Django app name
'base.custom_redirect_middleware.CustomRedirectFallbackMiddleware'

All the bellow cases working very fine.

With no URL parameters.

With one URL paramter.

With more than one URL paramters.

Update: Fixed raising of 404 when URL with no trailing slash.

Hope this helps.

like image 58
Steephan Selvaraj Avatar answered Oct 08 '22 10:10

Steephan Selvaraj