Commits

Jakub Zalewski  committed b4b7df1

slugify_unique

  • Participants
  • Parent commits 3417b89

Comments (0)

Files changed (1)

File annoying/models.py

+from datetime import datetime
 from django.db import models
-from datetime import datetime
+from django.template.defaultfilters import slugify
+
+
+def slugify_unique(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)[:47]
+        while True:
+            if suffix:
+                potential = "-".join([base, str(suffix)])
+            if model.objects.filter(**{slugfield: potential}).count() == 0:
+                return potential
+            # we hit a conflicting slug, so bump the suffix & try again
+            suffix += 1
 
 
 class Manager(models.Manager):
-    
+
     def _default_qs(self):
         return super(self.__class__, self).get_query_set()
-    
+
     def get_query_set(self):
         return self._default_qs().filter(is_deleted=False)
 
 
-class Model(models.Model):    
+class Model(models.Model):
     """base abstract model with common useful fields and delete flag instead of deletion"""
-    
+
     added = models.DateTimeField(auto_now_add=True)
     updated = models.DateTimeField(auto_now_add=True, auto_now=True)
     deleted = models.DateTimeField(null=True, blank=True)
-    
+
     version = models.IntegerField(default=1, editable=False)
     is_deleted = models.BooleanField(default=False, editable=False)
-        
+
     objects_all = models.Manager()
     objects = Manager()
-    
+
     def save(self, *args, **kwargs):
         if self.pk:
             if self.is_deleted:
             else:
                 self.version += 1
         super(Model, self).save(*args, **kwargs)
-    
+
     def delete(self, *args, **kwargs):
         self.is_deleted = True
         self.deleted = datetime.now()
         self.save(*args, **kwargs)
-    
-    class Meta:        
-        abstract = True        
+
+    class Meta:
+        abstract = True
         ordering = ['-id']
         get_latest_by = 'id'
-        
-        
+
+
 class TestModel(Model):
     """
     >>> t = TestModel.objects.create()
     2
     """
     test = models.TextField(null=True)
-    
-