[Fixed]-How can I use slugs in Django url

36👍

So you haven’t posted your code, but assuming your model looks like this:

class Post(models.Model):
    title = models.CharField(max_length=100)
    slug = models.SlugField(unique=True)
    content = models.TextField()

And that you want to pre-fill the slug from the title, you have a few options depending on where you want to do it:

  1. Post will be created only by staff users: pre-populate it in the admin
  2. Post will be created outside of the admin: override .save() method

From the admin

The easiest way to get this in the admin is via the prepopulated_fields

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    prepopulated_fields = {'slug': ('title',)}

Django will auto-update the slug field as you type the title when creating a post. Very nice UX, but limited to the admin…

Outside of the admin

In the previous example, you might end up with an empty slug if you were to create a post from the console, or from another page. In this case, you can quickly make sure slug is prepopulated by overriding the model’s .save() method, and calling slugify in it:

class Post(models.Model):
    title = models.CharField(max_length=100)
    slug = models.SlugField(unique=True)
    content = models.TextField()

    def save(self, *args, **kwargs):
        self.slug = self.slug or slugify(self.title)
        super().save(*args, **kwargs)

Link Post by slug

Disclaimer: if you need more details on this part, I suggest the part 3 of the official tutorial.

Provided you have a URL path:

# urls.py
from django.urls import path
from your_blog import views

urlpatterns = [
    path('posts/<slug:the_slug>/', views.post_detail_view, name='show_post'),
]

Then, in your views module you have a view:

# your_blog/views.py 
from django.views.generic.detail import DetailView


class PostDetailView(DetailView):
    model = Post
    # This file should exist somewhere to render your page
    template_name = 'your_blog/show_post.html'
    # Should match the value after ':' from url <slug:the_slug>
    slug_url_kwarg = 'the_slug'
    # Should match the name of the slug field on the model 
    slug_field = 'slug' # DetailView's default value: optional

post_detail_view = PostDetailView.as_view()

You can link to a Post by calling, in Python:

reverse('show_post', args=[the_post.slug])

Or in a Django template:

<a href="{% url 'show_post' the_post.slug %}">{{ the_post.title }}</a>

EDIT: Post index page

You could then add an index page, generating a list linking to all your posts:

# your_blog/views.py 
from django.views.generic import ListView


class PostListView(ListView):
    model = Post
    # This file should exist somewhere to render your page
    template_name = 'your_blog/list_post.html'

And in the view template:

<!-- your_blog/list_post.html -->
<ul>
  {% for the_post in object_list %}
    <li>
      <a href="{% url 'show_post' the_post.slug %}">{{ the_post.title }}</a>
    </li>
  {% endfor %}
</ul>

Hope that helps 🙂

Leave a comment