Support longer tag names.
[wolnelektury.git] / apps / newtagging / models.py
index 71cae93..694f5b8 100644 (file)
@@ -3,12 +3,11 @@
 Models and managers for generic tagging.
 """
 
-from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.fields import GenericForeignKey
 from django.contrib.contenttypes.models import ContentType
 from django.db import connection, models
 from django.utils.translation import ugettext_lazy as _
 from django.db.models.base import ModelBase
-from django.db.models.loading import get_model # D1.7: apps?
 from django.core.exceptions import ObjectDoesNotExist
 from django.dispatch import Signal
 
@@ -72,7 +71,7 @@ class TagManager(models.Manager):
             if tag not in current_tags:
                 self.intermediary_table_model._default_manager.create(tag=tag, content_object=obj)
 
-        tags_updated.send(sender=obj, affected_tags=tags_to_add + tags_for_removal)
+        tags_updated.send(sender=type(obj), instance=obj, affected_tags=tags_to_add + tags_for_removal)
 
     def remove_tag(self, obj, tag):
         """
@@ -105,6 +104,7 @@ class TagManager(models.Manager):
         of field lookups to be applied to the given Model as the
         ``filters`` argument.
         """
+        # TODO: Do we really need this filters stuff?
         if filters is None: filters = {}
 
         queryset = model._default_manager.filter()
@@ -159,18 +159,15 @@ class TaggedItemManager(models.Manager):
         """
         queryset, model = get_queryset_and_model(queryset_or_model)
         tags = self.tag_model.get_tag_list(tags)
-        tag_count = len(tags)
-        if not tag_count:
+        if not tags:
             # No existing tags were given
-            return queryset
-        elif tag_count == 1:
-            # Optimisation for single tag - fall through to the simpler
-            # query below.
-            return queryset.filter(tag_relations__tag=tags[0])
+            return queryset.none()
 
         # TODO: presumes reverse generic relation
-        return queryset.filter(tag_relations__tag__in=tags
-            ).annotate(count=models.Count('pk')).filter(count=len(tags))
+        # Multiple joins are WAY faster than having-count, at least on Postgres 9.1.
+        for tag in tags:
+            queryset = queryset.filter(tag_relations__tag=tag)
+        return queryset
 
     def get_union_by_model(self, queryset_or_model, tags):
         """
@@ -180,9 +177,9 @@ class TaggedItemManager(models.Manager):
         queryset, model = get_queryset_and_model(queryset_or_model)
         tags = self.tag_model.get_tag_list(tags)
         if not tags:
-            return queryset
+            return queryset.none()
         # TODO: presumes reverse generic relation
-        return queryset.filter(tag_relations__tag__in=tags)
+        return queryset.filter(tag_relations__tag__in=tags).distinct()
 
     def get_related(self, obj, queryset_or_model):
         """
@@ -194,7 +191,7 @@ class TaggedItemManager(models.Manager):
         # TODO: presumes reverse generic relation.
         # Do we know it's 'tags'?
         return queryset.filter(tag_relations__tag__in=obj.tags).annotate(
-            count=models.Count('pk')).order_by('-count').exclude(obj=obj.pk)
+            count=models.Count('pk')).order_by('-count').exclude(pk=obj.pk)
 
 
 ##########
@@ -222,7 +219,7 @@ def create_intermediary_table_model(model):
         'tag': models.ForeignKey(model, verbose_name=_('tag'), related_name='items'),
         'content_type': models.ForeignKey(ContentType, verbose_name=_('content type')),
         'object_id': models.PositiveIntegerField(_('object id'), db_index=True),
-        'content_object': generic.GenericForeignKey('content_type', 'object_id'),
+        'content_object': GenericForeignKey('content_type', 'object_id'),
         '__unicode__': obj_unicode,
     }
 
@@ -231,8 +228,8 @@ def create_intermediary_table_model(model):
 
 class TagMeta(ModelBase):
     "Metaclass for tag models (models inheriting from TagBase)."
-    def __new__(cls, name, bases, attrs):
-        model = super(TagMeta, cls).__new__(cls, name, bases, attrs)
+    def __new__(mcs, name, bases, attrs):
+        model = super(TagMeta, mcs).__new__(mcs, name, bases, attrs)
         if not model._meta.abstract:
             # Create an intermediary table and register custom managers for concrete models
             model.intermediary_table_model = create_intermediary_table_model(model)