Search code examples
pythondjangoslug

Using Slugify in Django urls


I'm trying to create SEO friendly urls where all spaces are replaced with a hyphen.

This is how I'm 'slugifying' the URL by using slugify in Django templates

<a href="{% url 'dj' dj_name=dj.name|slugify %}">

Here is my urls.py

url(r'^top100/(?P<dj_name>[a-zA-Z0-9 \'&-]+)/$', views.dj, name='dj')

This is my view

def dj(request, dj_name):
    dj = DJ.objects.get(name=dj_name)
    dj_song_list = Song.objects.filter(artist=dj, duplicate=False).order_by('-votes', '-release_date')
    return render(request, 'hunt/dj.html', {'dj_song_list': dj_song_list, 'dj':dj}

Now the %20 in the urls has changed to a - but I get the error DJ matching query does not exist.

Also this ignores & in the DJ name. For example it changes the url for the DJ Above & Beyond to www.example.com/top100/above-beyond


Solution

  • You're trying to request an object with its slug instead of its name in database. The slug is a string, compute from the original name, which you can use in URL (because it's SEO-friendly). But you can't request objects with it if you don't have save this slug anywhere in your database. Indeed, it's impossible to retrieve the original name from the slug.

    Above & Beyond --> above-beyond --> Above @ Beyond  }
                                    --> above & beyond  } A lot of possibilities...
                                    --> ABOVE - BEYOND  } 
                                    --> ...
    

    You need to use a SlugField() and get the object wanted according to this new field. Short example:

    class News(models.Model):
        title = models.CharField('title', max_length=100)
        slug = models.SlugField('slug', max_length=100, unique=True)
        content = models.TextField('news content')
    
        def get_absolute_url(self):
            return reverse('news-view', args=(self.slug, ))
    
    # In the app/urls.py:
    from . import views
    urlpatterns = [
        url(r'^(?P<slug>.+)/$', view.news_detail, name='news-view'),
        #...
    ]
    
    # In the 'news_detail' view (app/views.py)
    news = get_object_or_404(News, slug=slug)
    

    In practice, you can use the templatetags slugify if you want to use clean URL like stackoverflow: they're using the ID of the question to retrieve the content from the URL, but there's also the title, which you can change, it'll redirect you anyway.

    http://stackoverflow.com/questions/21377984/using-slugify-in-django-urls
                                       ^^^^^^^^
                                       ID used  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                                                slug just for SEO purpose, not
                                                used to retrieve from the db