Login

Automate unique slugs

Author:
taojian
Posted:
December 16, 2007
Language:
Python
Version:
.96
Tags:
slug save
Score:
3 (after 3 ratings)

If you want unique values for a slug field, but don't want to bother the user with error messages, this function can be put into a model's save function to automate unique slugs. It works by appending an integer counter to duplicate slugs.

The item's slug field is first prepopulated by slugify-ing the source field. If that value already exists, a counter is appended to the slug, and the counter incremented upward until the value is unique.

For instance, if you save an object titled Daily Roundup, and the slug daily-roundup is already taken, this function will try daily-roundup-2, daily-roundup-3, daily-roundup-4, etc, until a unique value is found.

Call from within a model's custom save() method like so: unique_slug(item, slug_source='field1', slug_field='field2') where the value of field slug_source will be used to prepopulate the value of slug_field.

Comments appreciated!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def unique_slug(item,slug_source,slug_field):
  """Ensures a unique slug field by appending an integer counter to duplicate slugs.
  
  The item's slug field is first prepopulated by slugify-ing the source field. If that value already exists, a counter is appended to the slug, and the counter incremented upward until the value is unique.
  
  For instance, if you save an object titled Daily Roundup, and the slug daily-roundup is already taken, this function will try daily-roundup-2, daily-roundup-3, daily-roundup-4, etc, until a unique value is found.
  
  Call from within a model's custom save() method like so:
  unique_slug(item, slug_source='field1', slug_field='field2')
  where the value of field slug_source will be used to prepopulate the value of slug_field.
  """
  if getattr(item, slug_field): # if it's already got a slug, do nothing.
      from django.template.defaultfilters import slugify
      slug = slugify(getattr(item,slug_source))
      itemModel = item.__class__
      # the following gets all existing slug values
      allSlugs = [sl.values()[0] for sl in itemModel.objects.values(slug_field)]
      if slug in allSlugs:
          import re
          counterFinder = re.compile(r'-\d+$')
          counter = 2
          slug = "%s-%i" % (slug, counter)
          while slug in allSlugs:
              slug = re.sub(counterFinder,"-%i" % counter, slug)
              counter += 1
      setattr(item,slug_field,slug)

More like this

  1. Automate unique slug (again) by davidwtbuxton 7 years ago
  2. Unique Slugify by SmileyChris 7 years, 1 month ago
  3. Counter model - run multiple persistent counters by simon 5 years, 11 months ago
  4. AutoSlugField by callipeo 7 years, 6 months ago
  5. YAAS (Yet Another Auto Slug) by carljm 7 years ago

Comments

kylefox (on December 16, 2007):

Cool. To make it more generic you could have the slug and title attribute names passed in as keyword arguments, and then use getattr and setattr to perform the logic, allowing:

unique_slug(post, slug_from="headline", slug_field="slug")
unique_slug(page, slug_from="title", slug_field="url")

#

taojian (on December 18, 2007):

I thought of that briefly, before being overcome by an attack of the lazies. But it should be done in the interest of portability and completeness; I'll work it out in the next few days.

Thanks

#

taojian (on December 23, 2007):

That was simpler than I'd thought.

#

taojian (on December 28, 2007):

The outer 'if' loop tests for an existing slug value, and leaves it alone if it's got one. Or rather, that's what it does now – when I made it possible to specify the name of the slug and source fields, I forgot to alter the test statement to use getattr().

Won't your function keep adding additional integer counters to the end of the slug, rather than incrementing a single counter?

I do wish this site had more search abilities. It's awfully hard to tell if you're posting duplicate snippets.

#

AdamKG (on April 23, 2008):

Um, the first line of the function body isn't supposed to be if not getattr...?

#

Please login first before commenting.