Permalink
Browse files

fixed: generating unique slug

  • Loading branch information...
1 parent 4041446 commit dc7f659c9364bbb640de087557d2b690e6cc64bd @marcinn committed Nov 28, 2010
Showing with 79 additions and 20 deletions.
  1. +3 −20 photogallery/models.py
  2. +76 −0 photogallery/utils.py
View
@@ -28,7 +28,7 @@
import tagging
import tagging.managers
-from photogallery.utils import slugify
+from photogallery.utils import slugify, unique_slugify
from photogallery.managers import *
from warnings import warn
@@ -152,25 +152,8 @@ def __unicode__(self):
def save(self, force_insert=False, force_update=False):
if not self.slug:
- if self.title:
- self.slug = slugify(self.title)
- # avoid duplicate slugs
- while self.__class__.objects.filter(
- slug=self.slug).count():
- match = self.re_slug.search(self.slug)
- if match:
- idx = int(match.group(1))
- self.slug = re.sub('-%d$' % idx, '-%d' % (idx+1),
- self.slug)
- else:
- self.slug = '%s-1' % self.slug
- else:
- _nonames = self.__class__.objects.filter(
- slug__istartswith=self.default_slug).count()
- if _nonames:
- self.slug = '%s-%d' % (self.default_slug, _nonames+1)
- else:
- self.slug = self.default_slug
+ slug_source = self.title or self.default_slug
+ unique_slugify(self, slug_source)
return super(Photo, self).save(force_insert, force_update)
def get_next_by_album(self):
View
@@ -1,4 +1,80 @@
+import re
try:
from netwizard.django.helpers import slugify
except ImportError:
from django.template.defaultfilters import slugify
+
+
+"""
+unique_slugify
+http://djangosnippets.org/snippets/690/
+"""
+
+def unique_slugify(instance, value, slug_field_name='slug', queryset=None,
+ slug_separator='-'):
+ """
+ Calculates and stores a unique slug of ``value`` for an instance.
+
+ ``slug_field_name`` should be a string matching the name of the field to
+ store the slug in (and the field to check against for uniqueness).
+
+ ``queryset`` usually doesn't need to be explicitly provided - it'll default
+ to using the ``.all()`` queryset from the model's default manager.
+ """
+ slug_field = instance._meta.get_field(slug_field_name)
+
+ slug = getattr(instance, slug_field.attname)
+ slug_len = slug_field.max_length
+
+ # Sort out the initial slug, limiting its length if necessary.
+ slug = slugify(value)
+ if slug_len:
+ slug = slug[:slug_len]
+ slug = _slug_strip(slug, slug_separator)
+ original_slug = slug
+
+ # Create the queryset if one wasn't explicitly provided and exclude the
+ # current instance from the queryset.
+ if queryset is None:
+ queryset = instance.__class__._default_manager.all()
+ if instance.pk:
+ queryset = queryset.exclude(pk=instance.pk)
+
+ # Find a unique slug. If one matches, at '-2' to the end and try again
+ # (then '-3', etc).
+ next = 2
+ while not slug or queryset.filter(**{slug_field_name: slug}):
+ slug = original_slug
+ end = '%s%s' % (slug_separator, next)
+ if slug_len and len(slug) + len(end) > slug_len:
+ slug = slug[:slug_len-len(end)]
+ slug = _slug_strip(slug, slug_separator)
+ slug = '%s%s' % (slug, end)
+ next += 1
+
+ setattr(instance, slug_field.attname, slug)
+
+
+def _slug_strip(value, separator='-'):
+ """
+ Cleans up a slug by removing slug separator characters that occur at the
+ beginning or end of a slug.
+
+ If an alternate separator is used, it will also replace any instances of
+ the default '-' separator with the new separator.
+ """
+ separator = separator or ''
+ if separator == '-' or not separator:
+ re_sep = '-'
+ else:
+ re_sep = '(?:-|%s)' % re.escape(separator)
+ # Remove multiple instances and if an alternate separator is provided,
+ # replace the default '-' separator.
+ if separator != re_sep:
+ value = re.sub('%s+' % re_sep, separator, value)
+ # Remove separator from the beginning and end of the slug.
+ if separator:
+ if separator != '-':
+ re_sep = re.escape(separator)
+ value = re.sub(r'^%s+|%s+$' % (re_sep, re_sep), '', value)
+ return value

0 comments on commit dc7f659

Please sign in to comment.