What is a slug in Django and How To Use It?


What is a slug in Django and How To Use It?

If you are working with Django or a similar web framework, you might have come across the term slug, slugField or slugify. What do these terms mean, why are slugs useful and how to use them properly? Where does the term slug come from?

A slug is a human-readable, unique label for a piece of data - usually an article or post. It’s most commonly used in urls, so it should contain no special characters.

In this tutorial I’ll quickly walk you through the “why”s and “how”s of Django slugs and also reveal the secret: what does an url identifier have to do with those slimy little creatures?

What is a slug?

Slug is just a regular string, with some restrictions: as it is very often used to build nice, human readable urls, it should not contain any special characters, that would be encoded when passed to the browser as an url.

Why Are Slugs Important

Accessibility - Human-readable URLs

Which one is better to look at:

https://pythonin1minute.com/what-is-a-slug-in-django/

or

https://pythonin1minute.com/articles?article_id=976f9bab-da45-4dd6-a5be-7e243472bd07

?

It is just a much nicer user experience to see meaningful, intelligible urls on a website.

Search Engine Optimization

Having human human readable URLs does help with technical SEO, as it is a basic requirement by search engines. Excerpt from Google’s SEO guidelines:

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). For example, if you’re searching for information about aviation, a URL like http://en.wikipedia.org/wiki/Aviation will help you decide whether to click that link. A URL like http://www.example.com/index.php?id_sezione=360&sid=3a5ebc944f41daa6f849f730f1, is much less appealing to users.

Consider using punctuation in your URLs. The URL http://www.example.com/green-dress.html is much more useful to us than http://www.example.com/greendress.html. We recommend that you use hyphens (-) instead of underscores (_) in your URLs.

So, if you have a Django app, that has some Article records in the database, then - from an SEO point-of-view - you are better off, if you look them up by some kind of meaningful identifier, then using just the auto-generated pk or uuid value.

In theory, you could have the article’s title directly included in the url and use that to look up the article, but the main problem with that approach would be that if you happen to update the title, that would mean the url also changes. You probably do not want that, as that would mean that all the external links to the page would be broken. From an SEO point-of-view that’s catastrophic. It would also mean that you need to update all your internal links as well. On top of that all users who might have bookmarked the page will probably be greeted with a 404 error page, as their bookmark would bring them to the old url.

For these reasons, it is considered good practice to keep webpages accessible on a permanent, unchanging link - a permalink.

How to Generate a Slug?

One possible (and actually the most commonly used) strategy to generate a slug for - let’s say - a blog post, is something like this:

  1. Take the title of the blog post
  2. Turn the whole title into lowercase
  3. Replace all whitespace characters with a hyphen (-). Note: Multiple consequent whitespace characters should also be replaced with one single hyphen: hello world should become hello-world not hello----world
  4. Remove all other special characters (only letters, numbers and hyphens are allowed)

For example: the title of this article is What is a slug in Django?. The generated slug - as you can see in the url - is what-is-a-slug-in-django

Let’s see how to do that in Django!

Slugs in Django - the SlugField

Django comes with a model field that’s aimed specifically at storing slugs. It’s called SlugField and actually it’s just a Charfield with some extra validation, that makes sure that there are no invalid characters in the string.

Let’s see how to use it in practice:

How to Add SlugField to a Django Model

Let’s say you have an article model with a title:

# models.py
from django.db import models 
  
class Article(models.Model): 
    title = models.CharField(max_length=200)

You can simply add the slug field like this:

# models.py
from django.db import models 
  
class Article(models.Model): 
    title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=200) 

Populating Slug Fields

Adding the field to your model does not do much good in itself, somehow you will need to populate these fields with values.

Auto-Populating in Admin

One way to generate values for you slug field is to add it to the prepopulated_fields in the admin class.

For example, for the Article model defined above, we can define an admin class like this:

# admin.py
from django.contrib import admin
from .models import Article

class ArticleAdmin(admin.ModelAdmin):
    prepopulated_fields = {"slug": ("title",)}

admin.site.register(Article, ArticleAdmin)

This will add a bit of javascript to the article admin page, that automatically generates a slug from the title. This is done only on creation, but not on update (in most cases we do not want to change the url, even if the title changes - see the section about permalinks)

If you go with this solution, the admin users will have the ability to change the generated slug manually.

As this is only a piece of client side code on the admin page, if the article is created programmatically, you will have to take care of the slug generation, otherwise it will remain empty.

Generating Slug Fields on Save

Another valid approach is to do the slug generation behind the scenes. You can override your Article model’s save method to generate a slug and populate the field with it.

To generate the slug value, we will use Django’s slugify utility.

Update your models file, like this:

# models.py
from django.utils.text import slugify

class Article(models.Model): 
    title = models.CharField(max_length=200)
    slug = models.SlugField()

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

The problem with this code is that it always regenerates the slug, whenever the title changes. We only want to do that on creation. (permalinks - remember?).

This issue is easily fixed, by checking the slug, and updating it only if it’s empty. The modified code looks like this:

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

class Article(models.Model): 
    title = models.CharField(max_length=200)
    slug = models.SlugField()

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

Ensure Uniqueness

This is looking good so far, but what happens if an admin creates two articles the would have the same slug? How would Django know which one to find?

To ensure that for each slug there is always only one record in the articles table in the database, we can add some constraints on the Article model’s slug field: we want it to be unique and we do not want to allow null values.

The model’s code now looks like this:

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

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

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

This ensures that no duplicate values are allowed for the slug column in the database. If you try to create a second one with an existing value, you will get an error.

Depending on the database backend, most likely the lookups will also be a bit faster, as the db engine will be able to use an index on the unique key.

Lookup

The lookup itself, is pretty simple, assuming that you are using django.views.generic.DetailView as the base class for your ArticleView class, you can just define the urls for your articles with slug as a keyword argument:

# urls.py
from django.urls import path

from .views import ArticleView

urlpatterns = [
    path('<slug:slug>', ArticleView.as_view())
]

Bonus: Where Does the Term “slug” Came From?

This all make sense, but why are they called slugs? As you made this far, here’s some fun trivia:

Actually, this term comes from newspaper publishing lingo - in the news room articles were referred to by a short, unique a name, to save time and avoid confusion.

The expression originated from printing/typesetting jargon. From the New York Times:

The term slug derives from the days of hot-metal printing, when printers set type by hand in a small form called a stick. Later huge Linotype machines turned molten lead into casts of letters, lines, sentences and paragraphs. A line of lead in both eras was known as a slug.

References

https://www.nytimes.com/times-insider/2014/11/24/whats-in-a-slug/ https://developers.google.com/search/docs/advanced/guidelines/url-structure?hl=en&visit_id=637415938585980856-1552768230&rd=1