Long overdue list of authors.
[wolnelektury.git] / src / newtagging / models.py
index 1d1b221..b90ea8e 100644 (file)
@@ -3,12 +3,9 @@
 Models and managers for generic tagging.
 """
 
-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.core.exceptions import ObjectDoesNotExist
 from django.dispatch import Signal
 
 qn = connection.ops.quote_name
@@ -65,10 +62,11 @@ class TagManager(models.Manager):
                 object_id=obj.pk,
                 tag__in=tags_for_removal).delete()
         # Add new tags
-        tags_to_add = [tag for tag in updated_tags
-                       if tag not in current_tags]
+        tags_to_add = [tag for tag in updated_tags if tag not in current_tags]
         for tag in tags_to_add:
-            if tag not in current_tags:
+            existing = self.intermediary_table_model.objects.filter(
+                content_type__pk=content_type.pk, object_id=obj.pk, tag=tag)
+            if not existing:
                 self.intermediary_table_model.objects.create(tag=tag, content_object=obj)
 
         tags_updated.send(sender=type(obj), instance=obj, affected_tags=tags_to_add + tags_for_removal)
@@ -81,6 +79,16 @@ class TagManager(models.Manager):
         self.intermediary_table_model.objects.filter(
             content_type__pk=content_type.pk, object_id=obj.pk, tag=tag).delete()
 
+    def add_tag(self, obj, tag):
+        """
+        Add tag to an object.
+        """
+        content_type = ContentType.objects.get_for_model(obj)
+        relations = self.intermediary_table_model.objects.filter(
+            content_type__pk=content_type.pk, object_id=obj.pk, tag=tag)
+        if not relations:
+            self.intermediary_table_model.objects.create(tag=tag, content_object=obj)
+
     def get_for_object(self, obj):
         """
         Create a queryset matching all tags associated with the given
@@ -197,42 +205,13 @@ class TaggedItemManager(models.Manager):
 ##########
 # Models #
 ##########
-def create_intermediary_table_model(model):
-    """Create an intermediary table model for the specific tag model"""
-    name = model.__name__ + 'Relation'
-
-    class Meta:
-        db_table = '%s_relation' % model._meta.db_table
-        unique_together = (('tag', 'content_type', 'object_id'),)
-        app_label = model._meta.app_label
-
-    def obj_unicode(self):
-        try:
-            return u'%s [%s]' % (self.content_type.get_object_for_this_type(pk=self.object_id), self.tag)
-        except ObjectDoesNotExist:
-            return u'<deleted> [%s]' % self.tag
-
-    # Set up a dictionary to simulate declarations within a class
-    attrs = {
-        '__module__': model.__module__,
-        'Meta': Meta,
-        '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': GenericForeignKey('content_type', 'object_id'),
-        '__unicode__': obj_unicode,
-    }
-
-    return type(name, (models.Model,), attrs)
-
 
 class TagMeta(ModelBase):
     """Metaclass for tag models (models inheriting from TagBase)."""
     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)
+            # Register custom managers for concrete models
             TagManager(model.intermediary_table_model).contribute_to_class(model, 'objects')
             TaggedItemManager(model).contribute_to_class(model.intermediary_table_model, 'objects')
         return model