Version 5 (modified by Loreana, 4 years ago) (diff)

--

It's common for me to want a unique slug based on a user supplied name of an object. Following the pattern outlined at djang-tips-auto-populated-fields and some useful instructions from research papers, it's easy to get the field populated without user intervention. However, by setting the slug field in the save() method the field isn't validated and thus unique=True restrictions aren't checked. I wrote the following code to attempt to pick slugs which are unique within a particular model. It's certainly not ideal (it suffers from race condition as well as potential for pounding a database with lots of small queries until it finds a good slug field), but it works for my small/mid-sized apps.

Feel free to suggest improvements to 'wam-djangowiki spam@…' (remove the ' spam' from the address there).

def SlugifyUniquely(value, model, slugfield="slug"):
        """Returns a slug on a name which is unique within a model's table

        This code suffers a race condition between when a unique
        slug is determined and when the object with that slug is saved.
        It's also not exactly database friendly if there is a high
        likelyhood of common slugs being attempted.

        A good usage pattern for this code would be to add a custom save()
        method to a model with a slug field along the lines of:

                from django.template.defaultfilters import slugify

                def save(self):
                    if not self.id:
                        # replace self.name with your prepopulate_from field
                        self.slug = SlugifyUniquely(self.name, self.__class__)
                super(self.__class__, self).save()

        Original pattern discussed at
        http://www.b-list.org/weblog/2006/11/02/django-tips-auto-populated-fields
        """
        suffix = 0
        potential = base = slugify(value)
        while True:
                if suffix:
                        potential = "-".join([base, str(suffix)])
                if not model.objects.filter(**{slugfield: potential}).count():
                        return potential
                # we hit a conflicting slug, so bump the suffix & try again
                suffix += 1
Back to Top