python

Auto-generate unique slugs in Django

Create unique, URL-friendly slugs on save.

from django.db import models
from django.utils.text import slugify

class Article(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=200, unique=True, blank=True)

    def save(self, *args, **kwargs):
        if not self.slug:
            base = slugify(self.title)[:180] or "item"
            slug, i = base, 2
            while Article.objects.filter(slug=slug).exclude(pk=self.pk).exists():
                slug = f"{base}-{i}"; i += 1
            self.slug = slug
        super().save(*args, **kwargs)

Why bother with slugs

A slug is the readable part of a URL: /blog/merge-excel-files/ instead of /blog/?id=47. People trust readable links more, they're easier to share, and search engines use the words in them.

How this works

When an Article is saved for the first time and has no slug, slugify turns the title into a URL-friendly version: lowercase, spaces become hyphens, symbols are dropped. So "Top 10 Excel Tips!" becomes top-10-excel-tips.

The while loop handles duplicates. If top-10-excel-tips is taken, it tries top-10-excel-tips-2, then -3, and so on. .exclude(pk=self.pk) makes sure an article doesn't count itself as a duplicate when it's saved again.

The [:180] cut keeps room for the -2 suffix, so the slug never overflows the field.

Things to know

  • Slugs don't change when the title changes. That's intentional. If the slug changed, every link anyone had shared would break. If you really must change one, set up a redirect from the old URL.
  • Titles in Hindi or other scripts come out empty from slugify, because by default it only keeps English letters. That's what the or "item" fallback is for. To keep the original characters, use slugify(self.title, allow_unicode=True).
  • Under heavy traffic, two saves at the exact same moment could pick the same slug. The unique=True on the field means the database will reject the second one rather than allow a duplicate. For a normal blog or catalogue, that's more than enough protection.