How to make a Django `url` case insensitive?

For example, if I visit http://localhost:8000/detail/PayPal I get a Page not found error 404 with the following message:

Using the URLconf ... Django tried these URL patterns, in this order:

...
detail/<slug:slug> [name='processor_detail']
The current path, detail/PayPal, matched the last one.

Here is my code: views.py:

class ProcessorDetailView(DetailView):
    model = Processor
    template_name = 'finder/processor_detail.html'
    slug_field = 'slug'  # Tell DetailView to use the `slug` model field as the DetailView slug
    slug_url_kwarg = 'slug'  # Match the URL parameter name

models.py:

class Processor(models.Model): #the newly created database model and below are the fields

    name = models.CharField(max_length=250, blank=True, null=True) #textField used for larger strings, CharField, smaller
    slug = models.SlugField(max_length=250, blank=True)
...
    def __str__(self): #displays some of the template information instead of 'Processot object'
        if self.name:
            return self.name[0:20]
        else:
            return '--no processor name listed--'
    def get_absolute_url(self): # new
        return reverse("processor_detail", args=[str(self.slug)])

    def save(self, *args, **kwargs): #`save` model a certain way(detailed in rest of function below)
        if not self.slug: #if there is no value in `slug` field then...
            self.slug = slugify(self.name) #...save a slugified `name` field value as the value in `slug` field
        super().save(*args, **kwargs)


urls.py: path("detail/<slug:slug>", views.ProcessorDetailView.as_view(), name='processor_detail')

I want that if I follow a link it either 1. doesn't matter what case I use or 2. the case in the browser url window changes to all lowercase.

Try to convert slug to lowercase using .lower()

You're getting 404, not because urls.py couldn't find a match, but because ProcessorDetailView couldn't find a slug named "PayPal", even if "paypal" was in the database.

So the problem isn't with urls.py, it's with the view trying to look for the slug you've specified. After some research, it turned out that you could make the model lookup case insensitive by using __iexact

Here's the modified views.py:

from django.views.generic.detail import DetailView
from .models import Processor
from django.http import Http404


class ProcessorDetailView(DetailView):
    
    def get_object(self, queryset=None):
        if queryset is None:
            queryset = self.get_queryset()
        
        slug = self.kwargs.get(self.slug_url_kwarg)
        if slug:
            slug_field = self.get_slug_field()
            queryset = queryset.filter(**{slug_field + '__iexact': slug})
            
        try:
            obj = queryset.get()
        except queryset.model.DoesNotExist:
            raise Http404("No %(verbose_name)s found matching the query" %
                        {'verbose_name': queryset.model._meta.verbose_name})
        return obj
    
    model = Processor
    template_name = 'finder/processor_detail.html'
    slug_field = 'slug'  # Tell DetailView to use the `slug` model field as the DetailView slug
    slug_url_kwarg = 'slug'  # Match the URL parameter name

Here we just overridden the get_object function, which results in modifying the queryset to be case insensitive. Check this out for more info on overriding get_object:

You can override the get_object method in ProcessorDetailView to ensure the slug is looked up in lowercase, like this:

from django.shortcuts import get_object_or_404

class ProcessorDetailView(DetailView):
    model = Processor
    template_name = 'finder/processor_detail.html'
    slug_field = 'slug'
    slug_url_kwarg = 'slug'

    def get_object(self, queryset=None):
        slug = self.kwargs.get(self.slug_url_kwarg).lower()
        return get_object_or_404(Processor, **{self.slug_field: slug})

That way, Django will match

http://localhost:8000/detail/PayPal or http://localhost:8000/detail/PayPal or any other case variations

Вернуться на верх