Prettier URLs with automatic slug generation 🐌
When building a website, sometimes you want a URL for a specific piece of data, but the URL should be nicer than /products/123
. A slug
can be used for a prettier URL like /products/really-cool-headphones
and, bonus points!, it's better for SEO as well.
SlugField
Django has a built-in SlugField
designed for this. It seems like the right approach, but you have to manually update the slug
unless you always use the built-in admin (in which case you can use prepopulated_fields
).
If you want the slug
generation to even more automated you will need an external library. Luckily there are two great ones to choose from.
django-extensions
The "kitchen sink" package that is django-extensions
has AutoSlugField
. The populate_from
kwarg for AutoSlugField
takes in a list of fields or model methods to generate a slug
.
from django.db import models
from django_extensions.db.fields import AutoSlugField
class Article(models.Model):
title = models.CharField(max_length=200)
author_name = models.CharField(max_length=200)
slug = AutoSlugField(populate_from=["title", "author_name","get_publish_date"])
def get_publish_date(self):
...
You can also use a custom method to create the slug
with the slugify_function
kwarg.
django-autoslug
django-autoslug
is a tiny focused package just for automatically generating slugs
.
from django.db import models
from autoslug import AutoSlugField
class Article(models.Model):
title = models.CharField(max_length=200)
slug = AutoSlugField(populate_from="title")
It also has some more specialized features which make sure that your slugs
are unique across the entire table of data. There are custom methods to create the slug as well.
Have your cake and eat it, too? 🎂
Another approach that sites like StackOverflow use is to have an identifier and the slug in the URL. Look at an example URL closely:
https://stackoverflow.com/questions/34230208/uuid-primary-key-in-postgres-what-insert-performance-impact
The slug at the end isn't used to look up the question at all. You can verify this if you don't believe me by changing the slug and going to the URL again.
/questions/324230208
is the question identifier and is the only part that is used to look up the specific question. Once the question has been found if the uuid-primary-key-in-postgres-what-insert-performance-impact
portion of the URL is different than the question's slug
in the database, they redirect to a URL with the correct slug
. This ensures that if the slug ever changes users will always get to the right URL.
This is one way to have nicer looking URLs, but handle if the slug
might change.
Keep a history of all slugs
forever
Another approach is to keep a list of slugs
that are generated as a foreign key (i.e. one-to-many). Inside your view, you would include that table in your query, but redirect to the latest slug if necessary.
def product(request, slug):
product = Product.objects.get(slugs__slug=slug)
last_created_slug = product.slugs.all().order_by("-created").first()
if last_created_slug.slug != slug:
return redirect("product", slug=last_created_slug.slug)
...
Personally, this approach seems like overkill and not worth the hassle.
My recommendation 🌟
Both of the additional libraries are great. Personally, I tend to:
- use
AutoSlugField
fromdjango-extensions
if I have already included it in the project and the slugs I need to create are straight-forward - only include
django-autoslug
if I need the additional functionality it provides
Related Content
Hi, I'm Adam 👋
I've been a backend programmer for ~20 years in a variety of different languages before I discovered Python 10 years ago and never looked back.
alldjango
includes all the hard-won experience I've gained over the years building production-scale Django websites.
Feel free to reach out to me on Mastodon or make a GitHub Issue with questions, comments, or bitter invectives.
All code is licensed as MIT.