pythondjangodjango-urls

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.


Solution

  • 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: