X-Git-Url: https://git.mdrn.pl/wolnelektury.git/blobdiff_plain/9c5d9a4e77a10b4e60d89d3890e49002bd7f3993..451f7b0e0ee3bfdab38cfa9c98a297c5320b52ad:/apps/newtagging/models.py diff --git a/apps/newtagging/models.py b/apps/newtagging/models.py index 1c35254bb..6d05e287e 100644 --- a/apps/newtagging/models.py +++ b/apps/newtagging/models.py @@ -15,6 +15,7 @@ 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 @@ -24,6 +25,8 @@ except ImportError: parse_lookup = None +tags_updated = Signal(providing_args=["affected_tags"]) + def get_queryset_and_model(queryset_or_model): """ Given a ``QuerySet`` or a ``Model``, returns a two-tuple of @@ -45,6 +48,16 @@ class TagManager(models.Manager): def __init__(self, intermediary_table_model): super(TagManager, self).__init__() self.intermediary_table_model = intermediary_table_model + models.signals.pre_delete.connect(self.target_deleted) + + def target_deleted(self, instance, **kwargs): + """ clear tag relations before deleting an object """ + try: + int(instance.pk) + except ValueError: + return + + self.update_tags(instance, []) def update_tags(self, obj, tags): """ @@ -63,10 +76,14 @@ class TagManager(models.Manager): object_id=obj.pk, tag__in=tags_for_removal).delete() # Add new tags - for tag in updated_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: 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) + def remove_tag(self, obj, tag): """ Remove tag from an object. @@ -198,8 +215,18 @@ class TagManager(models.Manager): if parse_lookup: raise AttributeError("'TagManager.usage_for_queryset' is not compatible with pre-queryset-refactor versions of Django.") - extra_joins = ' '.join(queryset.query.get_from_clause()[0][1:]) - where, params = queryset.query.where.as_sql() + if getattr(queryset.query, 'get_compiler', None): + # Django 1.2+ + compiler = queryset.query.get_compiler(using='default') + extra_joins = ' '.join(compiler.get_from_clause()[0][1:]) + where, params = queryset.query.where.as_sql( + compiler.quote_name_unless_alias, compiler.connection + ) + else: + # Django pre-1.2 + extra_joins = ' '.join(queryset.query.get_from_clause()[0][1:]) + where, params = queryset.query.where.as_sql() + if where: extra_criteria = 'AND %s' % where else: @@ -412,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 @@ -420,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) @@ -439,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 @@ -452,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()