# Django Uuslug
**A Django slugify application that guarantees `Uniqueness` and handles `Unicode`**
[![status-image]][status-link]
[![version-image]][version-link]
[![coverage-image]][coverage-link]
# Overview
In short: UUSlug == (`U`nique + `U`nicode) Slug
# How to install
1. easy_install django-uuslug
2. pip install django-uuslug
3. git clone http://github.com/un33k/django-uuslug
a. cd django-uuslug
b. run python setup.py
4. wget https://github.com/un33k/django-uuslug/zipball/master
a. unzip the downloaded file
b. cd into django-uuslug-* directory
c. run python setup.py
5. pip install -e git+https://github.com/un33k/django-uuslug#egg=django-uuslug
# How to use
```python
####### Unicode Test #######
from uuslug import slugify
txt = "This is a test ---"
r = slugify(txt)
self.assertEqual(r, "this-is-a-test")
txt = "___This is a test ---"
r = slugify(txt)
self.assertEqual(r, "this-is-a-test")
txt = "___This is a test___"
r = slugify(txt)
self.assertEqual(r, "this-is-a-test")
txt = "This -- is a ## test ---"
r = slugify(txt)
self.assertEqual(r, "this-is-a-test")
txt = '影師嗎'
r = slugify(txt)
self.assertEqual(r, "ying-shi-ma")
txt = 'C\'est déjà l\'été.'
r = slugify(txt)
self.assertEqual(r, "c-est-deja-l-ete")
txt = 'Nín hǎo. Wǒ shì zhōng guó rén'
r = slugify(txt)
self.assertEqual(r, "nin-hao-wo-shi-zhong-guo-ren")
txt = 'jaja---lol-méméméoo--a'
r = slugify(txt)
self.assertEqual(r, "jaja-lol-mememeoo-a")
txt = 'Компьютер'
r = slugify(txt)
self.assertEqual(r, "kompiuter")
txt = 'jaja---lol-méméméoo--a'
r = slugify(txt, max_length=9)
self.assertEqual(r, "jaja-lol")
txt = 'jaja---lol-méméméoo--a'
r = slugify(txt, max_length=15)
self.assertEqual(r, "jaja-lol-mememe")
txt = 'jaja---lol-méméméoo--a'
r = slugify(txt, max_length=50)
self.assertEqual(r, "jaja-lol-mememeoo-a")
txt = 'jaja---lol-méméméoo--a'
r = slugify(txt, max_length=15, word_boundary=True)
self.assertEqual(r, "jaja-lol-a")
txt = 'jaja---lol-méméméoo--a'
r = slugify(txt, max_length=17, word_boundary=True)
self.assertEqual(r, "jaja-lol-mememeoo")
txt = 'jaja---lol-méméméoo--a'
r = slugify(txt, max_length=18, word_boundary=True)
self.assertEqual(r, "jaja-lol-mememeoo")
txt = 'jaja---lol-méméméoo--a'
r = slugify(txt, max_length=19, word_boundary=True)
self.assertEqual(r, "jaja-lol-mememeoo-a")
txt = 'jaja---lol-méméméoo--a'
r = slugify(txt, max_length=20, word_boundary=True, separator=".")
self.assertEqual(r, "jaja.lol.mememeoo.a")
txt = 'jaja---lol-méméméoo--a'
r = slugify(txt, max_length=20, word_boundary=True, separator="ZZZZZZ")
self.assertEqual(r, "jajaZZZZZZlolZZZZZZmememeooZZZZZZa")
txt = 'one two three four five'
r = slugify(txt, max_length=13, word_boundary=True, save_order=True)
self.assertEqual(r, "one-two-three")
txt = 'one two three four five'
r = slugify(txt, max_length=13, word_boundary=True, save_order=False)
self.assertEqual(r, "one-two-three")
txt = 'one two three four five'
r = slugify(txt, max_length=12, word_boundary=True, save_order=False)
self.assertEqual(r, "one-two-four")
txt = 'one two three four five'
r = slugify(txt, max_length=12, word_boundary=True, save_order=True)
self.assertEqual(r, "one-two")
txt = 'this has a stopword'
r = slugify(txt, stopwords=['stopword'])
self.assertEqual(r, 'this-has-a')
txt = 'the quick brown fox jumps over the lazy dog'
r = slugify(txt, stopwords=['the'])
self.assertEqual(r, 'quick-brown-fox-jumps-over-lazy-dog')
txt = 'Foo A FOO B foo C'
r = slugify(txt, stopwords=['foo'])
self.assertEqual(r, 'a-b-c')
txt = 'Foo A FOO B foo C'
r = slugify(txt, stopwords=['FOO'])
self.assertEqual(r, 'a-b-c')
txt = 'the quick brown fox jumps over the lazy dog in a hurry'
r = slugify(txt, stopwords=['the', 'in', 'a', 'hurry'])
self.assertEqual(r, 'quick-brown-fox-jumps-over-lazy-dog')
####### Uniqueness Test #######
from django.db import models
from uuslug import uuslug
# Override your object's save method with something like this (models.py)
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 = uuslug(self.name, instance=self)
super(CoolSlug, self).save(*args, **kwargs)
# Note: You can also specify the start number.
# Example:
self.slug = uuslug(self.name, instance=self, start_no=2)
# the second slug should start with "-2" instead of "-1"
name = "john"
c = CoolSlug.objects.create(name=name)
c.save()
print(c.slug) # => "john"
c1 = CoolSlug.objects.create(name=name)
c1.save()
print(c1.slug) # => "john-1"
c2 = CoolSlug.objects.create(name=name)
c2.save()
print(c2.slug) # => "john-2"
# If you need truncation of your slug to exact length, here is an example
class SmartTruncatedSlug(models.Model):
name = models.CharField(max_length=19)
slug = models.CharField(max_length=10)
def __unicode__(self):
return self.name
def save(self, *args, **kwargs):
self.slug = uuslug(self.name, instance=self, max_length=10)
super(SmartTruncatedSlug, self).save(*args, **kwargs)
# If you need automatic truncation of your slug, here is an example
class AutoTruncatedSlug(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)
super(SmartTruncatedSlug, self).save(*args, **kwargs)
```
# Running the tests
To run the tests against the current environment:
python manage.py test
# License
Released under a ([BSD](LICENSE.md)) license.
# Version
X.Y.Z Version
`MAJOR` version -- when you make incompatible API changes,
`MINOR` version -- when you add functionality in a backwards-compatible manner, and
`PATCH` version -- when you make backwards-compatible bug fixes.
[status-image]: https://github.com/un33k/django-uuslug/actions/workflows/main.yml/badge.svg
[status-link]: https://github.com/un33k/django-uuslug/actions/workflows/main.yml
[status-image]: https://github.com/un33k/django-uuslug/actions/workflows/main.yml/badge.svg
[version-image]: https://img.shields.io/pypi/v/django-uuslug.svg
[version-link]: https://pypi.python.org/pypi/django-uuslug
[coverage-image]: https://coveralls.io/repos/un33k/django-uuslug/badge.svg
[coverage-link]: https://coveralls.io/r/un33k/django-uuslug
[download-image]: https://img.shields.io/pypi/dm/django-uuslug.svg
[download-link]: https://pypi.python.org/pypi/django-uuslug
# Sponsors
[Neekware Inc.](https://github.com/neekware)
Raw data
{
"_id": null,
"home_page": "https://github.com/un33k/django-uuslug",
"name": "django-uuslug",
"maintainer": "",
"docs_url": null,
"requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*",
"maintainer_email": "",
"keywords": "",
"author": "Val Neekman",
"author_email": "info@neekware.com",
"download_url": "https://files.pythonhosted.org/packages/20/7d/2be294809b7236a067f11947e7d4a3cf897ea65153fe95be3102d80894e6/django-uuslug-2.0.0.tar.gz",
"platform": "",
"description": "# Django Uuslug\n\n**A Django slugify application that guarantees `Uniqueness` and handles `Unicode`**\n\n[![status-image]][status-link]\n[![version-image]][version-link]\n[![coverage-image]][coverage-link]\n\n# Overview\n\nIn short: UUSlug == (`U`nique + `U`nicode) Slug\n\n# How to install\n\n 1. easy_install django-uuslug\n 2. pip install django-uuslug\n 3. git clone http://github.com/un33k/django-uuslug\n a. cd django-uuslug\n b. run python setup.py\n 4. wget https://github.com/un33k/django-uuslug/zipball/master\n a. unzip the downloaded file\n b. cd into django-uuslug-* directory\n c. run python setup.py\n 5. pip install -e git+https://github.com/un33k/django-uuslug#egg=django-uuslug\n\n# How to use\n\n```python\n\n ####### Unicode Test #######\n\n from uuslug import slugify\n\n txt = \"This is a test ---\"\n r = slugify(txt)\n self.assertEqual(r, \"this-is-a-test\")\n\n txt = \"___This is a test ---\"\n r = slugify(txt)\n self.assertEqual(r, \"this-is-a-test\")\n\n txt = \"___This is a test___\"\n r = slugify(txt)\n self.assertEqual(r, \"this-is-a-test\")\n\n txt = \"This -- is a ## test ---\"\n r = slugify(txt)\n self.assertEqual(r, \"this-is-a-test\")\n\n txt = '\u5f71\u5e2b\u55ce'\n r = slugify(txt)\n self.assertEqual(r, \"ying-shi-ma\")\n\n txt = 'C\\'est d\u00e9j\u00e0 l\\'\u00e9t\u00e9.'\n r = slugify(txt)\n self.assertEqual(r, \"c-est-deja-l-ete\")\n\n txt = 'N\u00edn h\u01ceo. W\u01d2 sh\u00ec zh\u014dng gu\u00f3 r\u00e9n'\n r = slugify(txt)\n self.assertEqual(r, \"nin-hao-wo-shi-zhong-guo-ren\")\n\n txt = 'jaja---lol-m\u00e9m\u00e9m\u00e9oo--a'\n r = slugify(txt)\n self.assertEqual(r, \"jaja-lol-mememeoo-a\")\n\n txt = '\u041a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440'\n r = slugify(txt)\n self.assertEqual(r, \"kompiuter\")\n\n txt = 'jaja---lol-m\u00e9m\u00e9m\u00e9oo--a'\n r = slugify(txt, max_length=9)\n self.assertEqual(r, \"jaja-lol\")\n\n txt = 'jaja---lol-m\u00e9m\u00e9m\u00e9oo--a'\n r = slugify(txt, max_length=15)\n self.assertEqual(r, \"jaja-lol-mememe\")\n\n txt = 'jaja---lol-m\u00e9m\u00e9m\u00e9oo--a'\n r = slugify(txt, max_length=50)\n self.assertEqual(r, \"jaja-lol-mememeoo-a\")\n\n txt = 'jaja---lol-m\u00e9m\u00e9m\u00e9oo--a'\n r = slugify(txt, max_length=15, word_boundary=True)\n self.assertEqual(r, \"jaja-lol-a\")\n\n txt = 'jaja---lol-m\u00e9m\u00e9m\u00e9oo--a'\n r = slugify(txt, max_length=17, word_boundary=True)\n self.assertEqual(r, \"jaja-lol-mememeoo\")\n\n txt = 'jaja---lol-m\u00e9m\u00e9m\u00e9oo--a'\n r = slugify(txt, max_length=18, word_boundary=True)\n self.assertEqual(r, \"jaja-lol-mememeoo\")\n\n txt = 'jaja---lol-m\u00e9m\u00e9m\u00e9oo--a'\n r = slugify(txt, max_length=19, word_boundary=True)\n self.assertEqual(r, \"jaja-lol-mememeoo-a\")\n\n txt = 'jaja---lol-m\u00e9m\u00e9m\u00e9oo--a'\n r = slugify(txt, max_length=20, word_boundary=True, separator=\".\")\n self.assertEqual(r, \"jaja.lol.mememeoo.a\")\n\n txt = 'jaja---lol-m\u00e9m\u00e9m\u00e9oo--a'\n r = slugify(txt, max_length=20, word_boundary=True, separator=\"ZZZZZZ\")\n self.assertEqual(r, \"jajaZZZZZZlolZZZZZZmememeooZZZZZZa\")\n\n txt = 'one two three four five'\n r = slugify(txt, max_length=13, word_boundary=True, save_order=True)\n self.assertEqual(r, \"one-two-three\")\n\n txt = 'one two three four five'\n r = slugify(txt, max_length=13, word_boundary=True, save_order=False)\n self.assertEqual(r, \"one-two-three\")\n\n txt = 'one two three four five'\n r = slugify(txt, max_length=12, word_boundary=True, save_order=False)\n self.assertEqual(r, \"one-two-four\")\n\n txt = 'one two three four five'\n r = slugify(txt, max_length=12, word_boundary=True, save_order=True)\n self.assertEqual(r, \"one-two\")\n\n txt = 'this has a stopword'\n r = slugify(txt, stopwords=['stopword'])\n self.assertEqual(r, 'this-has-a')\n\n txt = 'the quick brown fox jumps over the lazy dog'\n r = slugify(txt, stopwords=['the'])\n self.assertEqual(r, 'quick-brown-fox-jumps-over-lazy-dog')\n\n txt = 'Foo A FOO B foo C'\n r = slugify(txt, stopwords=['foo'])\n self.assertEqual(r, 'a-b-c')\n\n txt = 'Foo A FOO B foo C'\n r = slugify(txt, stopwords=['FOO'])\n self.assertEqual(r, 'a-b-c')\n\n txt = 'the quick brown fox jumps over the lazy dog in a hurry'\n r = slugify(txt, stopwords=['the', 'in', 'a', 'hurry'])\n self.assertEqual(r, 'quick-brown-fox-jumps-over-lazy-dog')\n\n\n ####### Uniqueness Test #######\n\n from django.db import models\n from uuslug import uuslug\n\n # Override your object's save method with something like this (models.py)\n class CoolSlug(models.Model):\n name = models.CharField(max_length=100)\n slug = models.CharField(max_length=200)\n\n def __unicode__(self):\n return self.name\n\n def save(self, *args, **kwargs):\n self.slug = uuslug(self.name, instance=self)\n super(CoolSlug, self).save(*args, **kwargs)\n\n # Note: You can also specify the start number.\n # Example:\n self.slug = uuslug(self.name, instance=self, start_no=2)\n # the second slug should start with \"-2\" instead of \"-1\"\n\n name = \"john\"\n c = CoolSlug.objects.create(name=name)\n c.save()\n print(c.slug) # => \"john\"\n\n c1 = CoolSlug.objects.create(name=name)\n c1.save()\n print(c1.slug) # => \"john-1\"\n\n c2 = CoolSlug.objects.create(name=name)\n c2.save()\n print(c2.slug) # => \"john-2\"\n\n\n # If you need truncation of your slug to exact length, here is an example\n class SmartTruncatedSlug(models.Model):\n name = models.CharField(max_length=19)\n slug = models.CharField(max_length=10)\n\n def __unicode__(self):\n return self.name\n\n def save(self, *args, **kwargs):\n self.slug = uuslug(self.name, instance=self, max_length=10)\n super(SmartTruncatedSlug, self).save(*args, **kwargs)\n\n # If you need automatic truncation of your slug, here is an example\n class AutoTruncatedSlug(models.Model):\n name = models.CharField(max_length=19)\n slug = models.CharField(max_length=19)\n\n def __unicode__(self):\n return self.name\n\n def save(self, *args, **kwargs):\n self.slug = uuslug(self.name, instance=self)\n super(SmartTruncatedSlug, self).save(*args, **kwargs)\n```\n\n# Running the tests\n\nTo run the tests against the current environment:\n\n python manage.py test\n\n# License\n\nReleased under a ([BSD](LICENSE.md)) license.\n\n# Version\n\nX.Y.Z Version\n\n `MAJOR` version -- when you make incompatible API changes,\n `MINOR` version -- when you add functionality in a backwards-compatible manner, and\n `PATCH` version -- when you make backwards-compatible bug fixes.\n\n[status-image]: https://github.com/un33k/django-uuslug/actions/workflows/main.yml/badge.svg\n[status-link]: https://github.com/un33k/django-uuslug/actions/workflows/main.yml\n[status-image]: https://github.com/un33k/django-uuslug/actions/workflows/main.yml/badge.svg\n[version-image]: https://img.shields.io/pypi/v/django-uuslug.svg\n[version-link]: https://pypi.python.org/pypi/django-uuslug\n[coverage-image]: https://coveralls.io/repos/un33k/django-uuslug/badge.svg\n[coverage-link]: https://coveralls.io/r/un33k/django-uuslug\n[download-image]: https://img.shields.io/pypi/dm/django-uuslug.svg\n[download-link]: https://pypi.python.org/pypi/django-uuslug\n\n# Sponsors\n\n[Neekware Inc.](https://github.com/neekware)\n\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A Django slugify application that also handles Unicode",
"version": "2.0.0",
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "5d88b411624363323c602a01b5c1fec94ce2b051498e93c566521debbaf15998",
"md5": "21184a2e34762d139bc91d734921e208",
"sha256": "5029077e9682db81a9f847cec9dc33c07f2e455e31f98931869e6220ca65a3e9"
},
"downloads": -1,
"filename": "django_uuslug-2.0.0-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "21184a2e34762d139bc91d734921e208",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*",
"size": 6413,
"upload_time": "2022-01-31T21:51:26",
"upload_time_iso_8601": "2022-01-31T21:51:26.531034Z",
"url": "https://files.pythonhosted.org/packages/5d/88/b411624363323c602a01b5c1fec94ce2b051498e93c566521debbaf15998/django_uuslug-2.0.0-py2.py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "207d2be294809b7236a067f11947e7d4a3cf897ea65153fe95be3102d80894e6",
"md5": "4200434ceb6a6eb54a3239460d971401",
"sha256": "047e713eeddecf11a674d4cd27ac72407f85ef13196856ba8dfeb4d691d521d4"
},
"downloads": -1,
"filename": "django-uuslug-2.0.0.tar.gz",
"has_sig": false,
"md5_digest": "4200434ceb6a6eb54a3239460d971401",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*",
"size": 7495,
"upload_time": "2022-01-31T21:51:27",
"upload_time_iso_8601": "2022-01-31T21:51:27.901994Z",
"url": "https://files.pythonhosted.org/packages/20/7d/2be294809b7236a067f11947e7d4a3cf897ea65153fe95be3102d80894e6/django-uuslug-2.0.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2022-01-31 21:51:27",
"github": true,
"gitlab": false,
"bitbucket": false,
"github_user": "un33k",
"github_project": "django-uuslug",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [
{
"name": "Django",
"specs": [
[
">=",
"2.2"
]
]
},
{
"name": "python-slugify",
"specs": [
[
">=",
"5.0.1"
]
]
}
],
"lcname": "django-uuslug"
}