better related books
authorRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Tue, 24 Jan 2012 12:06:37 +0000 (13:06 +0100)
committerRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Tue, 24 Jan 2012 12:06:37 +0000 (13:06 +0100)
apps/catalogue/templatetags/catalogue_tags.py
apps/newtagging/managers.py
apps/newtagging/models.py

index 583c9c7..2254299 100644 (file)
@@ -345,16 +345,9 @@ def related_books(book, limit=6):
         common_slug=book.common_slug).exclude(pk=book.pk)[:limit])
     limit -= len(related)
     if limit:
-        tagged = Book.tagged.with_any(book.tags).exclude(pk=book.pk)
-        book_tag = book.book_tag()
-        for rel_book in tagged:
-            if book_tag in rel_book.tags.all():
-                continue
-            related += [rel_book]
-            limit -= 1
-            if not limit:
-                break
-
+        related += Book.tagged.related_to(book,
+                Book.objects.exclude(common_slug=book.common_slug),
+                ignore_by_tag=book.book_tag())[:limit]
     return {
         'books': related,
     }
index 3107070..f802aa1 100644 (file)
@@ -35,11 +35,13 @@ class ModelTaggedItemManager(models.Manager):
         super(ModelTaggedItemManager, self).__init__()
         self.intermediary_table_model = tag_model.objects.intermediary_table_model
 
-    def related_to(self, obj, queryset=None, num=None):
+    def related_to(self, obj, queryset=None, num=None, ignore_by_tag=None):
         if queryset is None:
-            return self.intermediary_table_model.objects.get_related(obj, self.model, num=num)
+            return self.intermediary_table_model.objects.get_related(
+                obj, self.model, num=num, ignore_by_tag=ignore_by_tag)
         else:
-            return self.intermediary_table_model.objects.get_related(obj, queryset, num=num)
+            return self.intermediary_table_model.objects.get_related(
+                obj, queryset, num=num, ignore_by_tag=ignore_by_tag)
 
     def with_all(self, tags, queryset=None):
         if queryset is None:
index e9b1141..6d05e28 100644 (file)
@@ -439,7 +439,7 @@ class TaggedItemManager(models.Manager):
         else:
             return model._default_manager.none()
 
-    def get_related(self, obj, queryset_or_model, num=None):
+    def get_related(self, obj, queryset_or_model, num=None, ignore_by_tag=None):
         """
         Retrieve a list of instances of the specified model which share
         tags with the model instance ``obj``, ordered by the number of
@@ -447,6 +447,8 @@ class TaggedItemManager(models.Manager):
 
         If ``num`` is given, a maximum of ``num`` instances will be
         returned.
+
+        If ``ignore_by_tag`` is given, object tagged with it will be ignored.
         """
         queryset, model = get_queryset_and_model(queryset_or_model)
         model_table = qn(model._meta.db_table)
@@ -466,6 +468,15 @@ class TaggedItemManager(models.Manager):
             # instances for the same model.
             query += """
           AND related_tagged_item.object_id != %(tagged_item)s.object_id"""
+        if ignore_by_tag is not None:
+            query += """
+                AND NOT EXISTS (
+                    SELECT * FROM %(tagged_item)s
+                        WHERE %(tagged_item)s.object_id = %(model_pk)s
+                          AND %(tagged_item)s.content_type_id = %(content_type_id)s
+                          AND %(ignore_id)s = %(tagged_item)s.tag_id
+                )
+            """
         query += """
         GROUP BY %(model_pk)s
         ORDER BY %(count)s DESC
@@ -479,6 +490,7 @@ class TaggedItemManager(models.Manager):
             'content_type_id': content_type.pk,
             'related_content_type_id': related_content_type.pk,
             'limit_offset': num is not None and connection.ops.limit_offset_sql(num) or '',
+            'ignore_id': ignore_by_tag.id if ignore_by_tag else None,
         }
 
         cursor = connection.cursor()