Slug and PK based URL


(Shekhar Nunia) #1

Hello Frields,

I’m making a project in which I’m going to include many features like blog, QandA, Polls, challenge(like Quiz). You can consider it as a collection of few little project.
I was trying to make the url which contain the title of any app like title of blog or title of QandA, with it PK.
I read few docs and watch few videos about it but still confusing about it. Can anyone explain what is the best practice to achieve the slug and PK base url.

Thanks


(Utku) #2

Hi,

You can use hashids but I’m not sure that is the best practice. I’m really curious about this question. The performance is important too.

Thanks.


(Vitor Freitas) #3

The easiest approach would be using a regular slug field + the internal id of the model instance:

class Post(models.Model):
    title = models.CharField(max_length=100)
    slug = models.SlugField(max_length=100)

Then in your urls.py

from django.urls import path

urlpatterns = [
    path('blog/posts/<int:pk>-<slug:slug>/', views.blog_post, name='blog_post'),
]

In your views:

def blog_post(request, pk, slug):
    post = get_object_or_404(Post, pk=pk, slug=slug)
    # ...

So if you had a model with the following data:

id = 189
title = 'Slug and PK based URL'
slug = 'slug-and-pk-based-url'

The URL would be:

/blog/posts/189-slug-and-pk-based-url/

Actually since the pk is unique, you don’t really need the slug field saved in the database. You could generate it on the fly as a model method like:

from django.utils.text import slugify

class Post(models.Model):
    title = models.CharField(max_length=100)
    
    def get_slug(self):
        return slugify(self.title)

    def get_absolute_url(self):
        url = reverse('blog_post', kwargs={'pk': self.pk, 'slug': self.get_slug()})
        return url

The difference would be on your views.py where you would just get the post by the id, instead of also querying for the slug field:

def blog_post(request, pk, slug):
    post = get_object_or_404(Post, pk=pk)
    # ...

The slug would be ‘discarded’, just used as a pretty URL. Or you could perform a redirect just so you have a canonical URL:

from django.shortcuts import redirect

def blog_post(request, pk, slug):
    post = get_object_or_404(Post, pk=pk)
    if slug != post.get_slug():
        return redirect(post)  # will be redirected to the model's `get_absolute_url`
    # ...

But I would just store the slug on the database


(Shekhar Nunia) #4

Thanks you very much Vitor,
You just post a tutorial here and help us. I thinks this can help to many other also.
Thanks you


(Utku) #5

Hi,

How can I discard slug when I use this in a Class-Based View?

Thanks