Generating slugs automatically in Django without packages - Two easy and solid approaches

Slugs in Django
Image: Slugs in Django (License: CC-BY-SA)

Slug generation management

Last modified:


One important task when developing a Django website is to have pretty URLs, i.e.: human readable and SEO friendly URLs.

A site's URL structure should be as simple as possible. Consider organizing your content so that URLs are constructed logically and in a manner that is most intelligible to humans (when possible, readable words rather than long ID numbers)

Github repo available:


We will explore two approaches to have clean URLs, moving from typical URLs with the object/pk scheme, like /article/12345 to:

  • pk and slugs: object/pk-slug like /article/12345-my-example-title where we add the object’s slug after the primary key.

  • unique slug: generate a unique slugs without showing the primary key like /article/my-example-title

A slug is a short label for something, containing only letters, numbers, underscores or hyphens. They’re generally used in URLs..

Example app

The following app consisting of an Article model with a title will help to show how to use the pk-slug URL.

Example blog/

from django.views.generic.detail import DetailView

from blog.models import Article

class ArticleDetailView(DetailView):

    model = Article

Example blog/

from django.db import models

class Article(models.Model):
    title = models.CharField(max_length=100)

Example djangoslugs/

from django.urls import path

from blog.views import ArticleDetailView

urlpatterns = [
   path('blog/<int:pk>/', ArticleDetailView.as_view(), name='article-detail'),

First approach: PK and Slug


This approach increases Web Application security by avoiding an authorization attack: Insecure Direct Object References (IDOR)

Insecure Direct Object References occur when an application provides direct access to objects based on user-supplied input. As a result of this vulnerability attackers can bypass authorization and access resources in the system directly, for example database records or files.

This can be prevented by using both, an object primary key and the slug, as the Simple Object Mixin query_pk_and_slug docs states:

When applications allow access to individual objects by a sequential primary key, an attacker could brute-force guess all URLs; thereby obtaining a list of all objects in the application.

If users with access to individual objects should be prevented from obtaining this list, setting query_pk_and_slug to True will help prevent the guessing of URLs as each URL will require two correct, non-sequential arguments. Simply using a unique slug may serve the same purpose, but this scheme allows you to have non-unique slugs.


Many apps dedicated to manage slugs comes with a max_length restriction for its size and they truncate the string according to that value.

We are gonna use the same length as the slugified field.

1. Use SlugField

There is a special model Field type in Django for slugs: SlugField.

Create a field named slug with type: SlugField.

in blog/

from django.conf import settings
from django.db import models
from django.urls import reverse
from django.utils.text import slugify

class ArticlePkAndSlug(models.Model):
    title = models.CharField(
    slug = models.SlugField(

    def get_absolute_url(self):
        kwargs = {
            'slug': self.slug
        return reverse('article-pk-slug-detail', kwargs=kwargs)

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

URLs in djangoslugs/

    path('blog/<int:pk>-<str:slug>/', ArticleDetailView.as_view() , name='article-detail')

And in the view: blog/

class Article(DetailView):
    model = Article
    query_pk_and_slug = True

Before saving the instance, we convert the title to a slug with the slugify Django command, that basically replaces spaces by hyphens.

As SlugField inherits from CharField, it comes with the attribute max_length which handles the maximum length of the string it contains at database level.

If we use a SlugField without specifying its max_length attribute, it gets the value of 50 by default, which can lead to problems when we generate the string from a bigger max_length field.

So the trick is to make them both use the same length.

Second approach: unique slugs

In this case title and slug don’t need to have the same max_length, but this brings two issues when generating the slug:

  1. Truncate the slug to respect max_length
  2. Control the slug uniqueness by adding a suffix if another slug with the same string exists1

1. Truncate the slug

Before generating the slug, get the max_length value max_length = self._meta.get_field('slug').max_length and truncate at that position slug = slugify(self.title)[:max_length].

2. Ensure uniqueness

For each slug candidate we make sure it is unique by testing against the database until we have a non existing one.

import itertools


slug_candidate = slug_original = slugify(self.title)

for i in itertools.count(1):
	if not Article.objects.filter(slug=slug_candidate).exists():
	slug_candidate = '{}-{}'.format(slug_original, i)

All together

In save method:

import itertools

class ArticleUniqueSlug(Article):

    def save(self, *args, **kwargs):
        max_length = self._meta.get_field('slug').max_length
        value = self.title
        slug_candidate = slug_original = slugify(value, allow_unicode=True)
		for i in itertools.count(1):
			if not Article.objects.filter(slug=slug_candidate).exists():
			slug_candidate = '{}-{}'.format(slug_original, i)
		super().save(*args, **kwargs)

In view blog/

class Article(DetailView):
    model = ArticlePkAndSlug
    query_pk_and_slug = False

Github Repo

There is sample app showing each case at: Star

It has models and views for both approaches.

infolinks script]


Marcelo Canina
I'm Marcelo Canina, a developer from Uruguay. I build websites and web-based applications from the ground up and share what I learn here.
comments powered by Disqus

Guide to handle slugs for Django models focusing in just using Django's SlugField and avoiding third party apps/packages. Two approaches to solve it.

Clutter-free software concepts.

Except as otherwise noted, the content of this page is licensed under CC BY-NC-ND 4.0 .

Powered by SimpleIT Hugo Theme