Permalink
Browse files

added truncation support to uuslug and up the version

  • Loading branch information...
1 parent b8b9696 commit 34c50bdebc10cb154debf09b5426cfd1a9f84a84 Val Neekman committed Feb 13, 2013
Showing with 162 additions and 59 deletions.
  1. +31 −2 README.md
  2. +1 −1 requirements.txt
  3. +1 −1 setup.py
  4. +1 −1 testsettings.py
  5. +20 −40 uuslug/__init__.py
  6. +39 −0 uuslug/models.py
  7. +69 −14 uuslug/tests.py
View
33 README.md
@@ -87,8 +87,33 @@ Uniqueness Test
c2 = CoolSlug.objects.create(name=name)
c2.save()
print c2.slug # => "john-2"
-
-
+
+
+ # If you need truncation of your slug, here is an example
+ class SmartTruncatedSlug(models.Model):
+ name = models.CharField(max_length=19)
+ slug = models.CharField(max_length=19)
+
+ def __unicode__(self):
+ return self.name
+
+ def save(self, *args, **kwargs):
+ self.slug = uuslug(self.name, instance=self, start_no=9, max_length=19, word_boundary=True)
+ super(SmartTruncatedSlug, self).save(*args, **kwargs)
+
+ # Let's test it
+ name = 'jaja---lol-méméméoo--a'
+
+ obj = SmartTruncatedSlug.objects.create(name=name)
+ print obj.slug # "jaja-lol-mememeoo" --- where 19 is max_length (first slug, no duplicate yet)
+
+ obj = SmartTruncatedSlug.objects.create(name=name)
+ print obj.slug # "jaja-lol-mememeoo-9" --- where 19 is max_length, start_no = 9
+
+ obj = SmartTruncatedSlug.objects.create(name=name)
+ print obj.slug # "jaja-lol-mememeo-10" -- where 19 is max_length, smart appending "-10"
+
+
Running the tests
=================
@@ -100,6 +125,10 @@ To run the tests against the current environment:
Changelog
=========
+0.1.0
+-----
+* Added the ability to truncate slugs + tests (viva Juan Riaza of Spain)
+
0.9.0
-----
* removed buildout dependency
View
2 requirements.txt
@@ -1,3 +1,3 @@
Django>=1.3
-python-slugify>=0.0.2
+python-slugify>=0.0.3
View
2 setup.py
@@ -14,7 +14,7 @@
author = 'Val Neekman'
author_email = 'info@neekware.com'
license = 'BSD'
-install_requires = ['python-slugify>=0.0.2']
+install_requires = ['python-slugify>=0.0.3']
classifiers = [
'Development Status :: 4 - Beta',
'Environment :: Web Environment',
View
2 testsettings.py
@@ -4,5 +4,5 @@
'NAME': ':memory:',
},
}
-
+SECRET_KEY = "un33k"
INSTALLED_APPS = ['uuslug']
View
60 uuslug/__init__.py
@@ -1,51 +1,23 @@
# -*- coding: utf-8 -*-
-__version__ = '0.9.0'
+__version__ = '0.1.0'
from django.utils.encoding import smart_unicode
from slugify import slugify as pyslugify
__all__ = ['slugify', 'uuslug']
-def slugify(text, entities=True, decimal=True, hexadecimal=True):
+def slugify(text, entities=True, decimal=True, hexadecimal=True, max_length=0, word_boundary=False):
""" Make a slug from a given text """
- return smart_unicode(pyslugify(text, entities, decimal, hexadecimal))
+ return smart_unicode(pyslugify(text, entities, decimal, hexadecimal, max_length, word_boundary))
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)
- """
+ slug_field='slug', filter_dict=None, start_no=1, max_length=0, word_boundary=False):
+
+ """ This method tries a little harder than django's django.template.defaultfilters.slugify. """
+
if hasattr(instance, 'objects'):
raise Exception("Error: you must pass an instance to uuslug, not a model.")
@@ -55,12 +27,20 @@ def uuslug(s, instance, entities=True, decimal=True, hexadecimal=True,
if instance.pk:
queryset = queryset.exclude(pk=instance.pk)
- slug1 = slugify(s, entities=entities, decimal=decimal, hexadecimal=hexadecimal)
- slug2 = slug1
+ slug = slugify(s, entities=entities, decimal=decimal, hexadecimal=hexadecimal, max_length=max_length, word_boundary=word_boundary)
+ new_slug = slug
counter = start_no
- while queryset.filter(**{slug_field: slug2}).exists():
- slug2 = "%s-%s" % (slug1, counter)
+ while queryset.filter(**{slug_field: new_slug}).exists():
+ if max_length > 0:
+ if len(slug) + len('-') + len(str(counter)) > max_length:
+ slug = slug[:max_length-len(slug)-len('-')-len(str(counter))] # make room for the "-1, -2 ... etc"
+ new_slug = "%s-%s" % (slug, counter)
counter += 1
- return slug2
+ return new_slug
+
+
+
+
+
View
39 uuslug/models.py
@@ -29,3 +29,42 @@ def save(self, *args, **kwargs):
super(AnotherSlug, self).save(*args, **kwargs)
+ class TruncatedSlug(models.Model):
+ name = models.CharField(max_length=15)
+ slug = models.CharField(max_length=17)
+
+ def __unicode__(self):
+ return self.name
+
+ def save(self, *args, **kwargs):
+ self.slug = uuslug(self.name, instance=self, start_no=2, max_length=17, word_boundary=False)
+ super(TruncatedSlug, self).save(*args, **kwargs)
+
+
+ class SmartTruncatedSlug(models.Model):
+ name = models.CharField(max_length=17)
+ slug = models.CharField(max_length=17)
+
+ def __unicode__(self):
+ return self.name
+
+ def save(self, *args, **kwargs):
+ self.slug = uuslug(self.name, instance=self, start_no=2, max_length=17, word_boundary=True)
+ super(SmartTruncatedSlug, self).save(*args, **kwargs)
+
+
+ class SmartTruncatedExactWordBoundrySlug(models.Model):
+ name = models.CharField(max_length=19)
+ slug = models.CharField(max_length=19)
+
+ def __unicode__(self):
+ return self.name
+
+ def save(self, *args, **kwargs):
+ self.slug = uuslug(self.name, instance=self, start_no=9, max_length=19, word_boundary=True)
+ super(SmartTruncatedExactWordBoundrySlug, self).save(*args, **kwargs)
+
+
+
+
+
View
83 uuslug/tests.py
@@ -5,34 +5,63 @@
# http://pypi.python.org/pypi/django-tools/
#from django_tools.unittest_utils.print_sql import PrintQueries
-from uuslug.models import CoolSlug, AnotherSlug
+from uuslug.models import CoolSlug, AnotherSlug, TruncatedSlug, SmartTruncatedSlug, SmartTruncatedExactWordBoundrySlug
from uuslug import slugify
class SlugUnicodeTestCase(TestCase):
"""Tests for Slug - Unicode"""
def test_manager(self):
- s = "This is a test ---"
- r = slugify(s)
- self.assertEquals(r, "this-is-a-test")
- s = 'C\'est déjà l\'été.'
- r = slugify(s)
+ txt = "This is a test ---"
+ r = slugify(txt)
+ self.assertEquals(r, "this-is-a-test")
+
+ txt = "This -- is a ## test ---"
+ r = slugify(txt)
+ self.assertEquals(r, "this-is-a-test")
+
+ txt = 'C\'est déjà l\'été.'
+ r = slugify(txt)
self.assertEquals(r, "cest-deja-lete")
- s = 'Nín hǎo. Wǒ shì zhōng guó rén'
- r = slugify(s)
+ txt = 'Nín hǎo. Wǒ shì zhōng guó rén'
+ r = slugify(txt)
self.assertEquals(r, "nin-hao-wo-shi-zhong-guo-ren")
- s = '影師嗎'
- r = slugify(s)
- self.assertEquals(r, "ying-shi-ma")
-
- s = 'Компьютер'
- r = slugify(s)
+ txt = 'Компьютер'
+ r = slugify(txt)
self.assertEquals(r, "kompiuter")
+ txt = 'jaja---lol-méméméoo--a'
+ r = slugify(txt)
+ self.assertEquals(r, "jaja-lol-mememeoo-a")
+
+ txt = 'jaja---lol-méméméoo--a'
+ r = slugify(txt, max_length=9)
+ self.assertEquals(r, "jaja-lol")
+
+ txt = 'jaja---lol-méméméoo--a'
+ r = slugify(txt, max_length=15)
+ self.assertEquals(r, "jaja-lol-mememe")
+
+ txt = 'jaja---lol-méméméoo--a'
+ r = slugify(txt, max_length=50)
+ self.assertEquals(r, "jaja-lol-mememeoo-a")
+
+ txt = 'jaja---lol-méméméoo--a'
+ r = slugify(txt, max_length=15, word_boundary=True)
+ self.assertEquals(r, "jaja-lol-a")
+
+ txt = 'jaja---lol-méméméoo--a'
+ r = slugify(txt, max_length=19, word_boundary=True)
+ self.assertEquals(r, "jaja-lol-mememeoo")
+
+ txt = 'jaja---lol-méméméoo--a'
+ r = slugify(txt, max_length=20, word_boundary=True)
+ self.assertEquals(r, "jaja-lol-mememeoo-a")
+
class SlugUniqueTestCase(TestCase):
"""Tests for Slug - Unique"""
@@ -83,4 +112,30 @@ def test_start_no(self):
self.assertEquals(obj.slug, "foo-bar-3")
+ def test_max_length(self):
+ name = 'jaja---lol-méméméoo--a'
+
+ obj = TruncatedSlug.objects.create(name=name)
+ self.assertEquals(obj.slug, "jaja-lol-mememeoo") # 17 is max_length
+
+ obj = TruncatedSlug.objects.create(name=name)
+ self.assertEquals(obj.slug, "jaja-lol-mememe-2") # 17 is max_length
+
+ obj = TruncatedSlug.objects.create(name=name)
+ self.assertEquals(obj.slug, "jaja-lol-mememe-3") # 17 is max_length
+
+
+ def test_max_length_exact_word_boundry(self):
+ name = 'jaja---lol-méméméoo--a'
+
+ obj = SmartTruncatedExactWordBoundrySlug.objects.create(name=name)
+ self.assertEquals(obj.slug, "jaja-lol-mememeoo") # 19 is max_length
+
+ obj = SmartTruncatedExactWordBoundrySlug.objects.create(name=name)
+ self.assertEquals(obj.slug, "jaja-lol-mememeoo-9") # 19 is max_length, start_no = 9
+
+ obj = SmartTruncatedExactWordBoundrySlug.objects.create(name=name)
+ self.assertEquals(obj.slug, "jaja-lol-mememeo-10") # 19 is max_length, readjust for "-10"
+
+

0 comments on commit 34c50bd

Please sign in to comment.