Commit f759697f by Alex Gaynor

Added support for a custom slugify.

parent dccc744b
...@@ -142,10 +142,10 @@ class _TaggableManager(models.Manager): ...@@ -142,10 +142,10 @@ class _TaggableManager(models.Manager):
name__in=str_tags name__in=str_tags
) )
tag_objs.update(existing) tag_objs.update(existing)
for new_tag in str_tags - set(t.name for t in existing): for new_tag in str_tags - set(t.name for t in existing):
tag_objs.add(self.through.tag_model().objects.create(name=new_tag)) tag_objs.add(self.through.tag_model().objects.create(name=new_tag))
for tag in tag_objs: for tag in tag_objs:
self.through.objects.get_or_create(tag=tag, **self._lookup_kwargs()) self.through.objects.get_or_create(tag=tag, **self._lookup_kwargs())
......
...@@ -2,7 +2,7 @@ import django ...@@ -2,7 +2,7 @@ import django
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.generic import GenericForeignKey from django.contrib.contenttypes.generic import GenericForeignKey
from django.db import models, IntegrityError, transaction from django.db import models, IntegrityError, transaction
from django.template.defaultfilters import slugify from django.template.defaultfilters import slugify as default_slugify
from django.utils.translation import ugettext_lazy as _, ugettext from django.utils.translation import ugettext_lazy as _, ugettext
...@@ -18,7 +18,7 @@ class TagBase(models.Model): ...@@ -18,7 +18,7 @@ class TagBase(models.Model):
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if not self.pk and not self.slug: if not self.pk and not self.slug:
self.slug = slug = slugify(self.name) self.slug = self.slugify(self.name)
if django.VERSION >= (1, 2): if django.VERSION >= (1, 2):
from django.db import router from django.db import router
using = kwargs.get("using") or router.db_for_write( using = kwargs.get("using") or router.db_for_write(
...@@ -39,11 +39,17 @@ class TagBase(models.Model): ...@@ -39,11 +39,17 @@ class TagBase(models.Model):
return res return res
except IntegrityError: except IntegrityError:
transaction.savepoint_rollback(sid, **trans_kwargs) transaction.savepoint_rollback(sid, **trans_kwargs)
i += 1 self.slug = self.slugify(self.name, i)
self.slug = "%s_%d" % (slug, i)
else: else:
return super(TagBase, self).save(*args, **kwargs) return super(TagBase, self).save(*args, **kwargs)
def slugify(self, tag, i=None):
slug = default_slugify(tag)
if i is not None:
slug += "_%d" % i
return slug
class Tag(TagBase): class Tag(TagBase):
class Meta: class Meta:
verbose_name = _("Tag") verbose_name = _("Tag")
...@@ -137,4 +143,3 @@ class TaggedItem(GenericTaggedItemBase, TaggedItemBase): ...@@ -137,4 +143,3 @@ class TaggedItem(GenericTaggedItemBase, TaggedItemBase):
class Meta: class Meta:
verbose_name = _("Tagged Item") verbose_name = _("Tagged Item")
verbose_name_plural = _("Tagged Items") verbose_name_plural = _("Tagged Items")
from django.db import models from django.db import models
from taggit.managers import TaggableManager from taggit.managers import TaggableManager
from taggit.models import TaggedItemBase, GenericTaggedItemBase, TagBase from taggit.models import (TaggedItemBase, GenericTaggedItemBase, TaggedItem,
TagBase, Tag)
class Food(models.Model): class Food(models.Model):
...@@ -106,7 +107,7 @@ class OfficialHousePet(OfficialPet): ...@@ -106,7 +107,7 @@ class OfficialHousePet(OfficialPet):
class Media(models.Model): class Media(models.Model):
tags = TaggableManager() tags = TaggableManager()
class Meta: class Meta:
abstract = True abstract = True
...@@ -115,3 +116,27 @@ class Photo(Media): ...@@ -115,3 +116,27 @@ class Photo(Media):
class Movie(Media): class Movie(Media):
pass pass
class ArticleTag(Tag):
class Meta:
proxy = True
def slugify(self, tag, i=None):
slug = "category-%s" % tag
if i is not None:
slug += "-%d" % i
return slug
class ArticleTaggedItem(TaggedItem):
class Meta:
proxy = True
# Basically we want to overide the tag ForeignKey to point at the proxy
# inherited ArticleTag so we can get the right slugify, unfortunately I
# can't seem to figure out how to do this, so we're on hold ATM.
class Article(models.Model):
title = models.CharField(max_length=100)
tags = TaggableManager(through=ArticleTaggedItem)
...@@ -12,18 +12,18 @@ from taggit.tests.forms import (FoodForm, DirectFoodForm, CustomPKFoodForm, ...@@ -12,18 +12,18 @@ from taggit.tests.forms import (FoodForm, DirectFoodForm, CustomPKFoodForm,
from taggit.tests.models import (Food, Pet, HousePet, DirectFood, DirectPet, from taggit.tests.models import (Food, Pet, HousePet, DirectFood, DirectPet,
DirectHousePet, TaggedPet, CustomPKFood, CustomPKPet, CustomPKHousePet, DirectHousePet, TaggedPet, CustomPKFood, CustomPKPet, CustomPKHousePet,
TaggedCustomPKPet, OfficialFood, OfficialPet, OfficialHousePet, TaggedCustomPKPet, OfficialFood, OfficialPet, OfficialHousePet,
OfficialThroughModel, OfficialTag, Photo, Movie) OfficialThroughModel, OfficialTag, Photo, Movie, Article)
from taggit.utils import parse_tags, edit_string_for_tags from taggit.utils import parse_tags, edit_string_for_tags
class BaseTaggingTest(object): class BaseTaggingTest(object):
def assert_tags_equal(self, qs, tags, sort=True): def assert_tags_equal(self, qs, tags, sort=True, attr="name"):
got = map(lambda tag: tag.name, qs) got = map(lambda tag: getattr(tag, attr), qs)
if sort: if sort:
got.sort() got.sort()
tags.sort() tags.sort()
self.assertEqual(got, tags) self.assertEqual(got, tags)
def assert_num_queries(self, n, f, *args, **kwargs): def assert_num_queries(self, n, f, *args, **kwargs):
original_DEBUG = settings.DEBUG original_DEBUG = settings.DEBUG
settings.DEBUG = True settings.DEBUG = True
...@@ -61,6 +61,15 @@ class TagModelTestCase(BaseTaggingTransactionTestCase): ...@@ -61,6 +61,15 @@ class TagModelTestCase(BaseTaggingTransactionTestCase):
yummy = self.tag_model.objects.create(name="yummy") yummy = self.tag_model.objects.create(name="yummy")
apple.tags.add(yummy) apple.tags.add(yummy)
def test_slugify(self):
a = Article.objects.create(title="django-taggit 1.0 Released")
a.tags.add("awesome", "release", "AWESOME")
self.assert_tags_equal(a.tags.all(), [
"category-awesome",
"category-release",
"category-awesome-1"
], attr="slug")
class TagModelDirectTestCase(TagModelTestCase): class TagModelDirectTestCase(TagModelTestCase):
food_model = DirectFood food_model = DirectFood
tag_model = Tag tag_model = Tag
...@@ -113,7 +122,7 @@ class TaggableManagerTestCase(BaseTaggingTestCase): ...@@ -113,7 +122,7 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
apple.delete() apple.delete()
self.assert_tags_equal(self.food_model.tags.all(), ["green"]) self.assert_tags_equal(self.food_model.tags.all(), ["green"])
def test_add_queries(self): def test_add_queries(self):
apple = self.food_model.objects.create(name="apple") apple = self.food_model.objects.create(name="apple")
# 1 query to see which tags exist # 1 query to see which tags exist
...@@ -121,13 +130,13 @@ class TaggableManagerTestCase(BaseTaggingTestCase): ...@@ -121,13 +130,13 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
# + 6 queries to create the intermediary things (including SELECTs, to # + 6 queries to create the intermediary things (including SELECTs, to
# make sure we don't double create. # make sure we don't double create.
self.assert_num_queries(10, apple.tags.add, "red", "delicious", "green") self.assert_num_queries(10, apple.tags.add, "red", "delicious", "green")
pear = self.food_model.objects.create(name="pear") pear = self.food_model.objects.create(name="pear")
# 1 query to see which tags exist # 1 query to see which tags exist
# + 4 queries to create the intermeidary things (including SELECTs, to # + 4 queries to create the intermeidary things (including SELECTs, to
# make sure we dont't double create. # make sure we dont't double create.
self.assert_num_queries(5, pear.tags.add, "green", "delicious") self.assert_num_queries(5, pear.tags.add, "green", "delicious")
self.assert_num_queries(0, pear.tags.add) self.assert_num_queries(0, pear.tags.add)
def test_require_pk(self): def test_require_pk(self):
...@@ -245,7 +254,7 @@ class TaggableManagerTestCase(BaseTaggingTestCase): ...@@ -245,7 +254,7 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
unicode(self.taggeditem_model.objects.all()[0]), unicode(self.taggeditem_model.objects.all()[0]),
"ross tagged with president" "ross tagged with president"
) )
def test_abstract_subclasses(self): def test_abstract_subclasses(self):
p = Photo.objects.create() p = Photo.objects.create()
p.tags.add("outdoors", "pretty") p.tags.add("outdoors", "pretty")
...@@ -253,7 +262,7 @@ class TaggableManagerTestCase(BaseTaggingTestCase): ...@@ -253,7 +262,7 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
p.tags.all(), p.tags.all(),
["outdoors", "pretty"] ["outdoors", "pretty"]
) )
m = Movie.objects.create() m = Movie.objects.create()
m.tags.add("hd") m.tags.add("hd")
self.assert_tags_equal( self.assert_tags_equal(
...@@ -340,9 +349,9 @@ class TaggableFormTestCase(BaseTaggingTestCase): ...@@ -340,9 +349,9 @@ class TaggableFormTestCase(BaseTaggingTestCase):
self.assertEqual(ff.label, 'categories') self.assertEqual(ff.label, 'categories')
self.assertEqual(ff.help_text, u'Add some categories') self.assertEqual(ff.help_text, u'Add some categories')
self.assertEqual(ff.required, False) self.assertEqual(ff.required, False)
self.assertEqual(ff.clean(""), []) self.assertEqual(ff.clean(""), [])
tm = TaggableManager() tm = TaggableManager()
ff = tm.formfield() ff = tm.formfield()
self.assertRaises(ValidationError, ff.clean, "") self.assertRaises(ValidationError, ff.clean, "")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment