Auth+cache fixess
[wolnelektury.git] / src / catalogue / models / tag.py
index 02bf6c0..31da256 100644 (file)
@@ -3,13 +3,18 @@
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from django.conf import settings
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from django.conf import settings
+from django.contrib.contenttypes.fields import GenericForeignKey
+from django.contrib.contenttypes.models import ContentType
 from django.core.cache import caches
 from django.contrib.auth.models import User
 from django.core.cache import caches
 from django.contrib.auth.models import User
+from django.core.exceptions import ObjectDoesNotExist
 from django.db import models
 from django.db.models import permalink
 from django.db import models
 from django.db.models import permalink
+from django.db.models.query import Prefetch
 from django.dispatch import Signal
 from django.utils.translation import ugettext_lazy as _
 from django.dispatch import Signal
 from django.utils.translation import ugettext_lazy as _
-from newtagging.models import TagBase
+
+from newtagging.models import TagManager, TaggedItemManager
 from ssify import flush_ssi_includes
 
 
 from ssify import flush_ssi_includes
 
 
@@ -21,11 +26,31 @@ TAG_CATEGORIES = (
     ('genre', _('genre')),
     ('theme', _('theme')),
     ('set', _('set')),
     ('genre', _('genre')),
     ('theme', _('theme')),
     ('set', _('set')),
-    ('thing', _('thing')), # things shown on pictures
+    ('thing', _('thing')),  # things shown on pictures
 )
 
 
 )
 
 
-class Tag(TagBase):
+class TagRelation(models.Model):
+
+    tag = models.ForeignKey('Tag', 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')
+
+    objects = TaggedItemManager()
+
+    class Meta:
+        db_table = 'catalogue_tag_relation'
+        unique_together = (('tag', 'content_type', 'object_id'),)
+
+    def __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
+
+
+class Tag(models.Model):
     """A tag attachable to books and fragments (and possibly anything).
 
     Used to represent searchable metadata (authors, epochs, genres, kinds),
     """A tag attachable to books and fragments (and possibly anything).
 
     Used to represent searchable metadata (authors, epochs, genres, kinds),
@@ -33,10 +58,13 @@ class Tag(TagBase):
     name = models.CharField(_('name'), max_length=120, db_index=True)
     slug = models.SlugField(_('slug'), max_length=120, db_index=True)
     sort_key = models.CharField(_('sort key'), max_length=120, db_index=True)
     name = models.CharField(_('name'), max_length=120, db_index=True)
     slug = models.SlugField(_('slug'), max_length=120, db_index=True)
     sort_key = models.CharField(_('sort key'), max_length=120, db_index=True)
-    category = models.CharField(_('category'), max_length=50, blank=False, null=False,
-        db_index=True, choices=TAG_CATEGORIES)
+    category = models.CharField(
+        _('category'), max_length=50, blank=False, null=False, db_index=True, choices=TAG_CATEGORIES)
     description = models.TextField(_('description'), blank=True)
 
     description = models.TextField(_('description'), blank=True)
 
+    for_books = models.BooleanField(default=False)
+    for_pictures = models.BooleanField(default=False)
+
     user = models.ForeignKey(User, blank=True, null=True)
     gazeta_link = models.CharField(blank=True, max_length=240)
     culturepl_link = models.CharField(blank=True, max_length=240)
     user = models.ForeignKey(User, blank=True, null=True)
     gazeta_link = models.CharField(blank=True, max_length=240)
     culturepl_link = models.CharField(blank=True, max_length=240)
@@ -47,8 +75,13 @@ class Tag(TagBase):
 
     after_change = Signal(providing_args=['instance', 'languages'])
 
 
     after_change = Signal(providing_args=['instance', 'languages'])
 
+    intermediary_table_model = TagRelation
+    objects = TagManager()
+
     class UrlDeprecationWarning(DeprecationWarning):
     class UrlDeprecationWarning(DeprecationWarning):
-        pass
+        def __init__(self, tags=None):
+            super(Tag.UrlDeprecationWarning, self).__init__()
+            self.tags = tags
 
     categories_rev = {
         'autor': 'author',
 
     categories_rev = {
         'autor': 'author',
@@ -128,16 +161,25 @@ class Tag(TagBase):
     def __repr__(self):
         return "Tag(slug=%r)" % self.slug
 
     def __repr__(self):
         return "Tag(slug=%r)" % self.slug
 
+    def get_initial(self):
+        if self.category == 'author':
+            return self.sort_key[0]
+        elif self.name:
+            return self.name[0]
+        else:
+            return ''
+
+    @property
+    def category_plural(self):
+        return self.category + 's'
+
     @permalink
     def get_absolute_url(self):
     @permalink
     def get_absolute_url(self):
-        return ('catalogue.views.tagged_object_list', [self.url_chunk])
+        return 'tagged_object_list', [self.url_chunk]
 
 
-    @classmethod
     @permalink
     @permalink
-    def create_url(cls, category, slug):
-        return ('catalogue.views.tagged_object_list', [
-                '/'.join((cls.categories_dict[category], slug))
-            ])
+    def get_absolute_gallery_url(self):
+        return 'tagged_object_list_gallery', [self.url_chunk]
 
     def has_description(self):
         return len(self.description) > 0
 
     def has_description(self):
         return len(self.description) > 0
@@ -145,42 +187,39 @@ class Tag(TagBase):
     has_description.boolean = True
 
     @staticmethod
     has_description.boolean = True
 
     @staticmethod
-    def get_tag_list(tags):
-        if isinstance(tags, basestring):
-            real_tags = []
-            ambiguous_slugs = []
-            category = None
-            deprecated = False
-            tags_splitted = tags.split('/')
-            for name in tags_splitted:
-                if category:
-                    real_tags.append(Tag.objects.get(slug=name, category=category))
-                    category = None
-                elif name in Tag.categories_rev:
-                    category = Tag.categories_rev[name]
-                else:
-                    try:
-                        real_tags.append(Tag.objects.get(slug=name))
-                        deprecated = True
-                    except Tag.MultipleObjectsReturned, e:
-                        ambiguous_slugs.append(name)
-
+    def get_tag_list(tag_str):
+        if not tag_str:
+            return []
+        tags = []
+        ambiguous_slugs = []
+        category = None
+        deprecated = False
+        tags_splitted = tag_str.split('/')
+        for name in tags_splitted:
             if category:
             if category:
-                # something strange left off
-                raise Tag.DoesNotExist()
-            if ambiguous_slugs:
-                # some tags should be qualified
-                e = Tag.MultipleObjectsReturned()
-                e.tags = real_tags
-                e.ambiguous_slugs = ambiguous_slugs
-                raise e
-            if deprecated:
-                e = Tag.UrlDeprecationWarning()
-                e.tags = real_tags
-                raise e
-            return real_tags
-        else:
-            return TagBase.get_tag_list(tags)
+                tags.append(Tag.objects.get(slug=name, category=category))
+                category = None
+            elif name in Tag.categories_rev:
+                category = Tag.categories_rev[name]
+            else:
+                try:
+                    tags.append(Tag.objects.get(slug=name))
+                    deprecated = True
+                except Tag.MultipleObjectsReturned:
+                    ambiguous_slugs.append(name)
+
+        if category:
+            # something strange left off
+            raise Tag.DoesNotExist()
+        if ambiguous_slugs:
+            # some tags should be qualified
+            e = Tag.MultipleObjectsReturned()
+            e.tags = tags
+            e.ambiguous_slugs = ambiguous_slugs
+            raise e
+        if deprecated:
+            raise Tag.UrlDeprecationWarning(tags=tags)
+        return tags
 
     @property
     def url_chunk(self):
 
     @property
     def url_chunk(self):
@@ -188,28 +227,28 @@ class Tag(TagBase):
 
     @staticmethod
     def tags_from_info(info):
 
     @staticmethod
     def tags_from_info(info):
-        from fnpdjango.utils.text.slughifi import slughifi
+        from slugify import slugify
         from sortify import sortify
         meta_tags = []
         categories = (('kinds', 'kind'), ('genres', 'genre'), ('authors', 'author'), ('epochs', 'epoch'))
         for field_name, category in categories:
             try:
                 tag_names = getattr(info, field_name)
         from sortify import sortify
         meta_tags = []
         categories = (('kinds', 'kind'), ('genres', 'genre'), ('authors', 'author'), ('epochs', 'epoch'))
         for field_name, category in categories:
             try:
                 tag_names = getattr(info, field_name)
-            except:
+            except KeyError:
                 try:
                     tag_names = [getattr(info, category)]
                 try:
                     tag_names = [getattr(info, category)]
-                except:
+                except KeyError:
                     # For instance, Pictures do not have 'genre' field.
                     continue
             for tag_name in tag_names:
                 lang = getattr(tag_name, 'lang', settings.LANGUAGE_CODE)
                 tag_sort_key = tag_name
                 if category == 'author':
                     # For instance, Pictures do not have 'genre' field.
                     continue
             for tag_name in tag_names:
                 lang = getattr(tag_name, 'lang', settings.LANGUAGE_CODE)
                 tag_sort_key = tag_name
                 if category == 'author':
-                    tag_sort_key = tag_name.last_name
+                    tag_sort_key = ' '.join((tag_name.last_name,) + tag_name.first_names)
                     tag_name = tag_name.readable()
                 if lang == settings.LANGUAGE_CODE:
                     # Allow creating new tag, if it's in default language.
                     tag_name = tag_name.readable()
                 if lang == settings.LANGUAGE_CODE:
                     # Allow creating new tag, if it's in default language.
-                    tag, created = Tag.objects.get_or_create(slug=slughifi(tag_name), category=category)
+                    tag, created = Tag.objects.get_or_create(slug=slugify(tag_name), category=category)
                     if created:
                         tag_name = unicode(tag_name)
                         tag.name = tag_name
                     if created:
                         tag_name = unicode(tag_name)
                         tag.name = tag_name
@@ -229,5 +268,19 @@ class Tag(TagBase):
         return meta_tags
 
 
         return meta_tags
 
 
-# Pickle complains about not having this.
-TagRelation = Tag.intermediary_table_model
+TagRelation.tag_model = Tag
+
+
+def prefetch_relations(objects, category, only_name=True):
+    queryset = TagRelation.objects.filter(tag__category=category).select_related('tag')
+    if only_name:
+        queryset = queryset.only('tag__name_pl', 'object_id')
+    return objects.prefetch_related(
+        Prefetch('tag_relations', queryset=queryset, to_attr='%s_relations' % category))
+
+
+def prefetched_relations(obj, category):
+    if hasattr(obj, '%s_relations' % category):
+        return getattr(obj, '%s_relations' % category)
+    else:
+        return None