Sam
Sam

Reputation: 2084

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?

Upvotes: 6

Views: 2015

Answers (1)

Steephan Selvaraj
Steephan Selvaraj

Reputation: 1827

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.

Upvotes: 7

Related Questions