diff --git a/README b/README index 01b21e5..33a3d25 100644 --- a/README +++ b/README @@ -39,7 +39,7 @@ B. Test & contribute to django-uuslug: (for developers) Unicode Test Example ===================== -from uuslug import uuslug as slugify +from uuslug import slugify s = "This is a test ---" r = slugify(s) @@ -63,7 +63,7 @@ Uniqueness Test Example Override your object's save method with something like this (models.py) from django.db import models -from uuslug import uuslug as slugify +from uuslug import uuslug class CoolSlug(models.Model): name = models.CharField(max_length=100) @@ -73,9 +73,14 @@ class CoolSlug(models.Model): return self.name def save(self, *args, **kwargs): - self.slug = slugify(self.name, instance=self) + self.slug = uuslug(self.name, instance=self) super(CoolSlug, self).save(*args, **kwargs) + +You can specify the start number, e.g.: the second slug should start with "-2" instead of "-1": + self.slug = uuslug(self.name, instance=self, start_no=2) + + Test: ===== diff --git a/uuslug/__init__.py b/uuslug/__init__.py index 1b0beac..1ca81f8 100644 --- a/uuslug/__init__.py +++ b/uuslug/__init__.py @@ -18,7 +18,7 @@ pkg_resources.require("Unidecode") from unidecode import unidecode # only allow the import of our public APIs (UU-SLUG = Uniqure & Unicode Slug) -__all__ = ['uuslug'] +__all__ = ['slugify', 'uuslug'] # character entity reference @@ -34,46 +34,10 @@ REPLACE1_REXP = re.compile(r'[\']+') REPLACE2_REXP = re.compile(r'[^-a-z0-9]+') REMOVE_REXP = re.compile('-{2,}') - -def uuslug(s, entities=True, decimal=True, hexadecimal=True, - instance=None, slug_field='slug', filter_dict=None): - """This method tries a little harder than django's django.template.defaultfilters.slugify. - - Parameters - ---------- - s : string - Explanation - entities: boolean, optional - Explanation - decimal : boolean, optional - Explanation - hexadecimal : boolean, optional - Explanation - instance : Model object or None, optional - Explanation - slug_field : string, optional - Explanation - filter_dict : dictionary, optional - Explanation - - Returns - ------- - slug : string - Explanation - - Examples - -------- - Example usage in save method for model: - - import uuslug as slugify - self.slug = slugify(self.name, instance=self) - - Notes - ----- - - From http://www.djangosnippets.org/snippets/369/ +def slugify(s, entities=True, decimal=True, hexadecimal=True): + """ + make a slug from the given string """ - if type(s) != UnicodeType: s = unicode(s, 'utf-8', 'ignore') @@ -110,19 +74,62 @@ def uuslug(s, entities=True, decimal=True, hexadecimal=True, #remove redundant - s = REMOVE_REXP.sub('-', s).strip('-') - slug = s - if instance: - def get_query(): - if hasattr(instance, 'objects'): - raise Exception("Error: you must pass an instance to uuslug, not a model.") - query = instance.__class__.objects.filter(**{slug_field: slug}) - if filter_dict: - query = query.filter(**filter_dict) - if instance.pk: - query = query.exclude(pk=instance.pk) - return query - counter = 1 - while get_query(): - slug = "%s-%s" % (s, counter) - counter += 1 - return slug + return s + + +def uuslug(s, instance, entities=True, decimal=True, hexadecimal=True, + slug_field='slug', filter_dict=None, start_no=1): + """This method tries a little harder than django's django.template.defaultfilters.slugify. + + Parameters + ---------- + s : string + Explanation + entities: boolean, optional + Explanation + decimal : boolean, optional + Explanation + hexadecimal : boolean, optional + Explanation + instance : Model object or None, optional + Explanation + slug_field : string, optional + Explanation + filter_dict : dictionary, optional + Explanation + + Returns + ------- + slug : string + Explanation + + Examples + -------- + Example usage in save method for model: + + import uuslug as slugify + self.slug = slugify(self.name, instance=self) + + Notes + ----- + + From http://www.djangosnippets.org/snippets/369/ + """ + if hasattr(instance, 'objects'): + raise Exception("Error: you must pass an instance to uuslug, not a model.") + + queryset = instance.__class__.objects.all()#.only("pk", slug_field) + if filter_dict: + queryset = queryset.filter(**filter_dict) + if instance.pk: + queryset = queryset.exclude(pk=instance.pk) + + slug1 = slugify(s, entities=entities, decimal=decimal, hexadecimal=hexadecimal) + slug2 = slug1 + + counter = start_no + while queryset.filter(**{slug_field: slug2}).exists(): + slug2 = "%s-%s" % (slug1, counter) + counter += 1 + + return slug2 diff --git a/uuslug/models.py b/uuslug/models.py index b2ed54c..a772a32 100644 --- a/uuslug/models.py +++ b/uuslug/models.py @@ -3,17 +3,30 @@ import os # create a database table only in unit test mode if os.environ['DJANGO_SETTINGS_MODULE'] == 'uuslug.testsettings': from django.db import models - from uuslug import uuslug as slugify - + from uuslug import uuslug + + class CoolSlug(models.Model): name = models.CharField(max_length=100) slug = models.CharField(max_length=200) - + def __unicode__(self): return self.name - + def save(self, *args, **kwargs): - self.slug = slugify(self.name, instance=self) + self.slug = uuslug(self.name, instance=self) super(CoolSlug, self).save(*args, **kwargs) + class AnotherSlug(models.Model): + name = models.CharField(max_length=100) + slug = models.CharField(max_length=200) + + def __unicode__(self): + return self.name + + def save(self, *args, **kwargs): + self.slug = uuslug(self.name, instance=self, start_no=2) + super(AnotherSlug, self).save(*args, **kwargs) + + diff --git a/uuslug/tests/test_uuslug.py b/uuslug/tests/test_uuslug.py index ca2412b..5c96b98 100644 --- a/uuslug/tests/test_uuslug.py +++ b/uuslug/tests/test_uuslug.py @@ -1,9 +1,19 @@ -# -*- coding: utf-8 -*- +# coding: utf-8 + """Unit tests for uslug""" -from django.test import TestCase + +from django.db import models, connection from django.template import Context, Template -from uuslug.models import CoolSlug -from uuslug import uuslug as slugify +from django.test import TestCase +from django.db import connections, DEFAULT_DB_ALIAS, reset_queries +from django.core.signals import request_started + +# http://pypi.python.org/pypi/django-tools/ +#from django_tools.unittest_utils.print_sql import PrintQueries + +from uuslug.models import CoolSlug, AnotherSlug +from uuslug import slugify + class SlugUnicodeTestCase(TestCase): """Tests for Slug - Unicode""" @@ -25,19 +35,51 @@ class SlugUnicodeTestCase(TestCase): r = slugify(s) self.assertEquals(r, "ying-shi-ma") + + class SlugUniqueTestCase(TestCase): """Tests for Slug - Unique""" - def test_manager(self): name = "john" - c = CoolSlug.objects.create(name=name) - c.save() - self.assertEquals(c.slug, name) - - c1 = CoolSlug.objects.create(name=name) - c1.save() - self.assertEquals(c1.slug, name+"-1") + #with PrintQueries("create first john"): # display the SQL queries + with self.assertNumQueries(2): + # 1. query: SELECT test, if slug 'john' exists + # 2. query: INSERT values + obj = CoolSlug.objects.create(name=name) + self.assertEquals(obj.slug, "john") + #with PrintQueries("create second john"): # display the SQL queries + with self.assertNumQueries(3): + # 1. query: SELECT test, if slug 'john' exists + # 2. query: SELECT test, if slug 'john-1' exists + # 3. query: INSERT values + obj = CoolSlug.objects.create(name=name) + self.assertEquals(obj.slug, "john-1") + def test_start_no(self): + name = 'Foo Bar'#'C\'est déjà l\'été.' + #with PrintQueries("create first 'Foo Bar'"): # display the SQL queries + with self.assertNumQueries(2): + # 1. query: SELECT test, if slug 'foo-bar' exists + # 2. query: INSERT values + obj = AnotherSlug.objects.create(name=name) + self.assertEquals(obj.slug, "foo-bar") + + #with PrintQueries("create second 'Foo Bar'"): # display the SQL queries + with self.assertNumQueries(3): + # 1. query: SELECT test, if slug 'foo-bar' exists + # 2. query: SELECT test, if slug 'foo-bar-2' exists + # 3. query: INSERT values + obj = AnotherSlug.objects.create(name=name) + self.assertEquals(obj.slug, "foo-bar-2") + + #with PrintQueries("create third 'Foo Bar'"): # display the SQL queries + with self.assertNumQueries(4): + # 1. query: SELECT test, if slug 'foo-bar' exists + # 2. query: SELECT test, if slug 'foo-bar-2' exists + # 3. query: SELECT test, if slug 'foo-bar-3' exists + # 4. query: INSERT values + obj = AnotherSlug.objects.create(name=name) + self.assertEquals(obj.slug, "foo-bar-3")