Automatic Slug Generator

Technical
December 1, 2011 6:22 pm

Any object that can be “browsed” to in a Django application needs a friendly URL. Whether it be a blog post, a calendar entry, and event or a user’s profile, referring to them by their ID or Primary Key is not going to help search engines find you.

Although Django provides an excellent Slug field type, and the admin site can automatically populate the slug field, it falls short on two fronts. First, it doesn’t create a slug for user created objects. Second, it does not enforce unique slugs. This is fine for a generic framework, but if your visitors are creating content that needs a slug, i.e. topics in a forum, or their profile pages, you need to automatically generate slugs.

This snippet of code does just that. It works using a signal, making it reusable and applicable to any model in a Django applications. The only requirement is that the model have a “title” (from which the slug is generated) and a “slug” attribute (to store the slug). The slug must have the keyword, unique=True, and it must raise an IntegrityError when trying to save a non-unique object. This is the default behaviour in Django, so as long as your slug has unique=True, you’re good to go.

Ask for forgiveness, not for permission

The key principle here is the idea of asking for forgiveness, not for permission. What do I mean? Well, it would seem logical in Django to simply slugify the title of an instance, and then try to load an object with that slug. If you can load it, it already exists, therefore try another slug. If it doesn’t load it, then you can use this slug. In other words, our application asks if a slug is already in use before it tries to use it.

In the vast majority of cases, titles are already unique and will not cause clashes, therefore, we’re better off simply slugifying the title then saving it, relying on our model to throw back an exception if we can’t use it. Only when this exception is thrown do we need to worry about changing the slug, thus giving (an admittedly modest) performance gain.

The Code

Dump this code in to any file that makes sense to you. I like to store it somewhere like core.utils, but the point here is that it’s a generic library, so just make sure it’s on the Python Path.

from django.db import IntegrityError
from django.template.defaultfilters import slugify

def find_available_slug(object, instance, slug):
    """
    Recursive method that will add underscores to a slug field
    until a free value is located
    """
    instance.slug = slug
    try:
        instance.save()
        return
    except IntegrityError:
        slug = '%s_' % slug
        find_available_slug(object, instance, slug)

def slug_generator(sender, **kwargs):
    """ Generates a unique slug for a node """
    instance = kwargs['instance']
    if instance.slug is not '':
        return
    slug = slugify(instance.title)
    find_available_slug(sender, instance, slug)

Usage

from django.db.models.signals import post_save
from core.utils import slug_generator

class MyModel(models.Model):
     title = models.CharField(max_length=255)
     slug = models.SlugField(blank=True, null=True, unique=True)

post_save.connect(slug_generator, sender=MyModel)
This article was written by on Thursday, December 1, 2011 at 6:22 pm. You can follow any responses to this entry through the RSS feed. You can leave a response, or trackback from your own site. Tags:

Leave a Reply


XCOM: Enemy Unknown

October 21, 2012
XCOM: Enemy Unknown

XCOM: Enemy Unknown is the latest modernisation of a classic game developed by Firaxis and published by 2k. Although the franchise has a colourful history of cancelled games and relatively unheard of releases, it’s heyday was back in the mid-nineties with the original Enemy Unknown and the sequel, Terror from the Deep, being the titles that most gamers will be familiar with. Their premise was as simple then as it is now. Aliens have started invading a near future earth [...]

Continue Reading →

Asking for forgivness, not permission

July 19, 2012

Sometimes in life it’s easier to be forgiven for something than it is to get permission. It’s an approach I’ve been trying to adopt in my programming lately in a way that sometimes feels a little counter intuitive. If, for example, you need to ensure something going in to a database needs to be unique you may do something like this: def generate_slug(self): if not self.slug: self.slug = slugify(self.title) try: Page.objects.get(slug=self.slug) self.slug = self.slug + "_" self.generate_slug() except Page.DoesNotExist: self.save() [...]

Continue Reading →

Eurogamer Expo 2012

October 1, 2012
Eurogamer Expo 2012

2012 was my second year at Eurogamer, the first being last year, but it was no less spectacular second time round. I only went on the Saturday this year, mostly because I booked my ticket too late, but also because I wasn’t so interested in the games on offer this year. There were tonnes of triple A titles on show, as well as the usual mix of merchandise, retro gaming, t-shirts and the like. But if I’m honest, the games [...]

Continue Reading →

The Beat-Herder Festival – 2012

April 15, 2012
The Beat-Herder Festival – 2012

Starting to get quite excited about this year’s Beat-Herder Festival! 6 or so friends have confirmed, and I have my ticket + parking pass. More of the lineup has been trickling out  over the last few weeks and I’m most exited to be seeing Slamboree, Lee Scratch Perry and Parov Stelar. There’s also loads of stuff on that I’ve never heard of, so hopefully I’ll bring home some new names to listen to afterwards. Anyone who has not heard of [...]

Continue Reading →