Example #1
0
    def get_by_model(self, queryset_or_model, tags):
        """
        Create a ``QuerySet`` containing instances of the specified
        model associated with a given tag or list of tags.
        """
        tags = get_tag_list(tags)
        tag_count = len(tags)
        if tag_count == 0:
            # No existing tags were given
            queryset, model = get_queryset_and_model(queryset_or_model)
            return model._default_manager.none()
        elif tag_count == 1:
            # Optimisation for single tag - fall through to the simpler
            # query below.
            tag = tags[0]
        else:
            return self.get_intersection_by_model(queryset_or_model, tags)

        queryset, model = get_queryset_and_model(queryset_or_model)
        content_type = ContentType.objects.get_for_model(model)
        opts = self.model._meta
        tagged_item_table = qn(opts.db_table)
        return queryset.extra(
            tables=[opts.db_table],
            where=[
                '%s.content_type_id = %%s' % tagged_item_table,
                '%s.tag_id = %%s' % tagged_item_table,
                '%s.%s = %s.object_id' %
                (qn(model._meta.db_table), qn(
                    model._meta.pk.column), tagged_item_table)
            ],
            params=[content_type.pk, tag.pk],
        )
Example #2
0
    def get_by_model(self, queryset_or_model, tags):
        """
        Create a ``QuerySet`` containing instances of the specified
        model associated with a given tag or list of tags.
        """
        tags = get_tag_list(tags)
        tag_count = len(tags)
        if tag_count == 0:
            # No existing tags were given
            queryset, model = get_queryset_and_model(queryset_or_model)
            return model._default_manager.none()
        elif tag_count == 1:
            # Optimisation for single tag - fall through to the simpler
            # query below.
            tag = tags[0]
        else:
            return self.get_intersection_by_model(queryset_or_model, tags)

        queryset, model = get_queryset_and_model(queryset_or_model)
        content_type = ContentType.objects.get_for_model(model)
        opts = self.model._meta
        tagged_item_table = qn(opts.db_table)
        return queryset.extra(
            tables=[opts.db_table],
            where=[
                '%s.content_type_id = %%s' % tagged_item_table,
                '%s.tag_id = %%s' % tagged_item_table,
                '%s.%s = %s.object_id' % (qn(model._meta.db_table),
                                          qn(model._meta.pk.column),
                                          tagged_item_table)
            ],
            params=[content_type.pk, tag.pk],
        )
Example #3
0
    def get_related(self, obj, queryset_or_model, num=None):
        """
        Retrieve a list of instances of the specified model which share
        tags with the model instance ``obj``, ordered by the number of
        shared tags in descending order.

        If ``num`` is given, a maximum of ``num`` instances will be
        returned.
        """
        queryset, model = get_queryset_and_model(queryset_or_model)
        model_table = qn(model._meta.db_table)
        content_type = ContentType.objects.get_for_model(obj)
        related_content_type = ContentType.objects.get_for_model(model)
        query = """
        SELECT %(model_pk)s, COUNT(related_tagged_item.object_id) AS %(count)s
        FROM %(model)s, %(tagged_item)s, %(tag)s, %(tagged_item)s related_tagged_item
        WHERE %(tagged_item)s.object_id = %%s
          AND %(tagged_item)s.content_type_id = %(content_type_id)s
          AND %(tag)s.id = %(tagged_item)s.tag_id
          AND related_tagged_item.content_type_id = %(related_content_type_id)s
          AND related_tagged_item.tag_id = %(tagged_item)s.tag_id
          AND %(model_pk)s = related_tagged_item.object_id"""
        if content_type.pk == related_content_type.pk:
            # Exclude the given instance itself if determining related
            # instances for the same model.
            query += """
          AND related_tagged_item.object_id != %(tagged_item)s.object_id"""
        query += """
        GROUP BY %(model_pk)s
        ORDER BY %(count)s DESC
        %(limit_offset)s"""
        query = query % {
            "model_pk": "%s.%s" % (model_table, qn(model._meta.pk.column)),
            "count": qn("count"),
            "model": model_table,
            "tagged_item": qn(self.model._meta.db_table),
            "tag": qn(self.model._meta.get_field("tag").rel.to._meta.db_table),
            "content_type_id": content_type.pk,
            "related_content_type_id": related_content_type.pk,
            # Hardcoding this for now just to get tests working again - this
            # should now be handled by the query object.
            "limit_offset": num is not None and ("LIMIT %s" % num) or "",
        }

        cursor = connection.cursor()
        params = [obj.pk]
        if num is not None:
            params.append(num)
        cursor.execute(query, params)
        object_ids = [row[0] for row in cursor.fetchall()]
        if len(object_ids) > 0:
            # Use in_bulk here instead of an id__in lookup, because id__in would
            # clobber the ordering.
            object_dict = queryset.in_bulk(object_ids)
            return [
                object_dict[object_id] for object_id in object_ids
                if object_id in object_dict
            ]
        else:
            return []
Example #4
0
    def get_no_tags(self, queryset_or_model):
        """
        Create a ``QuerySet`` containing instances of the specified
        model that contain no tags
        """
        queryset, model = get_queryset_and_model(queryset_or_model)
        model_table = qn(model._meta.db_table)
        # This query selects the ids of all objects which have no tags
        query = """
        SELECT %(model_pk)s
        FROM %(model)s
            left outer join %(tagged_item)s on %(model_pk)s = %(tagged_item)s.object_id and %(tagged_item)s.content_type_id = %(content_type_id)s
        WHERE  %(tagged_item)s.tag_id is null
        GROUP BY %(model_pk)s""" % {
            'model_pk': '%s.%s' % (model_table, qn(model._meta.pk.column)),
            'model': model_table,
            'tagged_item': qn(self.model._meta.db_table),
            'content_type_id': ContentType.objects.get_for_model(model).pk,
        }

        cursor = connection.cursor()
        cursor.execute(query)
        object_ids = [row[0] for row in cursor.fetchall()]
        if len(object_ids) > 0:
            return queryset.filter(pk__in=object_ids)
        else:
            return model._default_manager.none()
Example #5
0
    def get_union_by_model(self, queryset_or_model, tags):
        """
        Create a ``QuerySet`` containing instances of the specified
        model associated with *any* of the given list of tags.

        get_union_by_model is more efficent than get_intersection_by_model.
        """
        queryset, model = get_queryset_and_model(queryset_or_model)
        content_type = ContentType.objects.get_for_model(model)

        items = self._get_item_list(tags, content_type)

        ## TODO: This does not work, because when the same item has
        ## several of the tags supplied, that item's id shows up twice.
        ## This is not standard django behavior, and is specific to gae
        #object_ids = [item.object_id for item in items]
        ## This works, however
        object_ids = set()
        for item in items:
            object_ids.add(item.object_id)

        if len(object_ids) > 0:
            return queryset.filter(pk__in=object_ids)
        elif len(object_ids) == 1:
            return queryset.filter(pk=items[0].object_id)
        else:
            return model._default_manager.none()
Example #6
0
    def get_union_by_model(self, queryset_or_model, tags):
        """
        Create a ``QuerySet`` containing instances of the specified
        model associated with *any* of the given list of tags.
        """
        tags = get_tag_list(tags)
        tag_count = len(tags)
        queryset, model = get_queryset_and_model(queryset_or_model)
        model_table = qn(model._meta.db_table)
        # This query selects the ids of all objects which have any of
        # the given tags.
        query = """
        SELECT %(model_pk)s
        FROM %(model)s, %(tagged_item)s
        WHERE %(tagged_item)s.content_type_id = %(content_type_id)s
          AND %(tagged_item)s.tag_id IN (%(tag_id_placeholders)s)
          AND %(model_pk)s = %(tagged_item)s.object_id
        GROUP BY %(model_pk)s""" % {
            'model_pk': '%s.%s' % (model_table, qn(model._meta.pk.column)),
            'model': model_table,
            'tagged_item': qn(self.model._meta.db_table),
            'content_type_id': ContentType.objects.get_for_model(model).pk,
            'tag_id_placeholders': ','.join(['%s'] * tag_count),
        }

        cursor = connection.cursor()
        cursor.execute(query, [tag.pk for tag in tags])
        object_ids = [row[0] for row in cursor.fetchall()]
        if len(object_ids) > 0:
            return queryset.filter(pk__in=object_ids)
        else:
            return model._default_manager.none()
Example #7
0
    def get_related(self, obj, queryset_or_model, num=None):
        """
        Retrieve a list of instances of the specified model which share
        tags with the model instance ``obj``, ordered by the number of
        shared tags in descending order.

        If ``num`` is given, a maximum of ``num`` instances will be
        returned.
        """
        queryset, model = get_queryset_and_model(queryset_or_model)
        model_table = qn(model._meta.db_table)
        content_type = ContentType.objects.get_for_model(obj)
        related_content_type = ContentType.objects.get_for_model(model)
        query = """
        SELECT %(model_pk)s, COUNT(related_tagged_item.object_id) AS %(count)s
        FROM %(model)s, %(tagged_item)s, %(tag)s, %(tagged_item)s related_tagged_item
        WHERE %(tagged_item)s.object_id = %%s
          AND %(tagged_item)s.content_type_id = %(content_type_id)s
          AND %(tag)s.id = %(tagged_item)s.tag_id
          AND related_tagged_item.content_type_id = %(related_content_type_id)s
          AND related_tagged_item.tag_id = %(tagged_item)s.tag_id
          AND %(model_pk)s = related_tagged_item.object_id"""
        if content_type.pk == related_content_type.pk:
            # Exclude the given instance itself if determining related
            # instances for the same model.
            query += """
          AND related_tagged_item.object_id != %(tagged_item)s.object_id"""
        query += """
        GROUP BY %(model_pk)s
        ORDER BY %(count)s DESC
        %(limit_offset)s"""
        query = query % {
            'model_pk': '%s.%s' % (model_table, qn(model._meta.pk.column)),
            'count': qn('count'),
            'model': model_table,
            'tagged_item': qn(self.model._meta.db_table),
            'tag': qn(self.model._meta.get_field('tag').rel.to._meta.db_table),
            'content_type_id': content_type.pk,
            'related_content_type_id': related_content_type.pk,
            # Hardcoding this for now just to get tests working again - this
            # should now be handled by the query object.
            'limit_offset': num is not None and 'LIMIT %s' or '',
        }

        cursor = connection.cursor()
        params = [obj.pk]
        if num is not None:
            params.append(num)
        cursor.execute(query, params)
        object_ids = [row[0] for row in cursor.fetchall()]
        if len(object_ids) > 0:
            # Use in_bulk here instead of an id__in lookup,
            # because id__in would
            # clobber the ordering.
            object_dict = queryset.in_bulk(object_ids)
            return [object_dict[object_id] for object_id in object_ids
                    if object_id in object_dict]
        else:
            return []
Example #8
0
    def get_context_data(self, **kwargs):
        context = super(TaggedObjectList, self).get_context_data(**kwargs)
        context['tag'] = self.tag_instance

        if self.related_tags:
            queryset, model = get_queryset_and_model(self.queryset_or_model)
            context['related_tags'] = Tag.objects.related_for_model(
                self.tag_instance, model, counts=self.related_tag_counts)
        return context
    def get_context_data(self, **kwargs):
        context = super(TaggedObjectList, self).get_context_data(**kwargs)
        context['tag'] = self.tag_instance

        if self.related_tags:
            queryset, model = get_queryset_and_model(self.queryset_or_model)
            context['related_tags'] = Tag.objects.related_for_model(
                self.tag_instance, model, counts=self.related_tag_counts)
        return context
Example #10
0
 def get_by_model(self, queryset_or_model, tags, include_synonyms=True):
     # TODO: we may remove include synonyms as the preferred synonym is forced when tagging
     """
     Create a ``QuerySet`` containing instances of the specified
     model associated with a given tag or list of tags.
     """
     tags = get_tag_list(tags)
     tag_count = len(tags)
     if tag_count == 0:
         # No existing tags were given
         queryset, model = get_queryset_and_model(queryset_or_model)
         return model._default_manager.none()
     elif tag_count == 1:
         # Optimization for single tag - fall through to the simpler
         # query below.
         tag = tags[0]
         if include_synonyms:
             related_tags = tag.get_related(relation_types=['=', '=>', '<='])
             if related_tags.count() > 0:
                 # we have synonyms; 1 tag & n synonyms; return the union
                 return self.get_union_by_model(queryset_or_model, [tag] + list(related_tags))
         # No synonyms; go on with the usual way
         queryset, model = get_queryset_and_model(queryset_or_model)
         content_type = ContentType.objects.get_for_model(model)
         db_table = self.model._meta.db_table
         tagged_item_table = qn(db_table)
         return queryset.extra(
             tables=[db_table],
             where=[
                 '%s.content_type_id = %%s' % tagged_item_table,
                 '%s.tag_id = %%s' % tagged_item_table,
                 '%s.%s = %s.object_id' % (qn(model._meta.db_table),
                                           qn(model._meta.pk.column),
                                           tagged_item_table)
             ],
             params=[content_type.pk, tag.pk],
         )
     else:
         # we have multiple tags
         return self.get_intersection_by_model(queryset_or_model, tags, 
                                               include_synonyms=include_synonyms)
Example #11
0
    def get_related(self, obj, queryset_or_model, num=None):
        """
        Retrieve a list of instances of the specified model which share
        tags with the model instance ``obj``, ordered by the number of
        shared tags in descending order.

        If ``num`` is given, a maximum of ``num`` instances will be
        returned.
        """
        queryset, model = get_queryset_and_model(queryset_or_model)

        ## (1) Query 1, grab the items for the specified object
        content_type = ContentType.objects.get_for_model(obj)
        obj_items = self.filter(content_type=content_type, object_id=obj.pk)

        ## (2) Query 2, grab the items that share the same tags as that first list
        if not isinstance(obj, model):
            content_type = ContentType.objects.get_for_model(model)
            tag_ids = [item.tag_id for item in obj_items]
            tag_items = self.filter(content_type=content_type, tag__pk__in=tag_ids)
        else:
            tag_ids = [item.tag_id for item in obj_items]
            tag_items = self.filter(content_type=content_type, tag__pk__in=tag_ids) \
                            .exclude(object_id=obj.pk)

        ## (3) Aggregate and sort the results
        pk_map = {}
        ## TODO: This if test is required because of a bug in django-nonrel
        ## where empty iterators raise IndexErrors (in djangoappengine/db/compiler.py:285). 
        ## I put tag_items in a list because of another django-nonrel bug.
        tag_items = list(tag_items)
        if len(tag_items) > 0:
            for item in tag_items:
                count = pk_map.get(item.object_id, 0)
                pk_map[item.object_id] = count + 1

            object_ids = sorted(pk_map.keys(), key=lambda k: pk_map[k], reverse=True)
            if num is not None:
                object_ids = object_ids[0:num]
        else:
            return []
        
        ## (4) Create the final list of sorted objects
        if len(object_ids) > 0:
            # Use in_bulk here instead of an id__in lookup, because id__in would
            # clobber the ordering.
            object_dict = queryset.in_bulk(object_ids)
            ## TODO: using int() here because in_bulk somehow is changing the id to int.
            ## My concern is that this behavior may be specific to gae -legutierr
            return [object_dict[int(object_id)] for object_id in object_ids \
                    if int(object_id) in object_dict]
        else:
            return []
Example #12
0
    def get_intersection_by_model(self, queryset_or_model, tags, include_synonyms=True):
        """
        Create a ``QuerySet`` containing instances of the specified
        model associated with *all* of the given list of tags.
        """
        tags = get_tag_list(tags)
        tag_count = len(tags)

        if not tag_count:
            return model._default_manager.none()
        
        # replace the tags with their preferred synonyms if they exist
        temp_tags = []
        for tag in tags:
            try:
                rt = RelatedTag.objects.get(tag=tag, relation_type='=>')
            except RelatedTag.DoesNotExist:
                temp_tags.append(tag)
            else:
                temp_tags.append(rt.related_tag)
        
        # make sure the tags are unique
        tags = list(set(temp_tags))

        queryset, model = get_queryset_and_model(queryset_or_model)

        model_table = qn(model._meta.db_table)
        # This query selects the ids of all objects which have all the
        # given tags.
        query = """
        SELECT %(model_pk)s
        FROM %(model)s, %(tagged_item)s
        WHERE %(tagged_item)s.content_type_id = %(content_type_id)s
          AND %(tagged_item)s.tag_id IN (%(tag_id_placeholders)s)
          AND %(model_pk)s = %(tagged_item)s.object_id
        GROUP BY %(model_pk)s
        HAVING COUNT(%(model_pk)s) = %(tag_count)s""" % {
            'model_pk': '%s.%s' % (model_table, qn(model._meta.pk.column)),
            'model': model_table,
            'tagged_item': qn(self.model._meta.db_table),
            'content_type_id': ContentType.objects.get_for_model(model).pk,
            'tag_id_placeholders': ','.join(['%s'] * tag_count),
            'tag_count': tag_count,
        }
        print query, ','.join(['%s'] * tag_count), [tag.pk for tag in tags]

        cursor = connection.cursor()
        cursor.execute(query, [tag.pk for tag in tags])
        object_ids = [row[0] for row in cursor.fetchall()]
        if len(object_ids) > 0:
            return queryset.filter(pk__in=object_ids)
        else:
            return model._default_manager.none()
Example #13
0
    def get_related(self, obj, queryset_or_model, num=None):
        """
        Retrieve a list of instances of the specified model which share
        tags with the model instance ``obj``, ordered by the number of
        shared tags in descending order.

        If ``num`` is given, a maximum of ``num`` instances will be
        returned.
        """
        queryset, model = get_queryset_and_model(queryset_or_model)
        model_table = qn(model._meta.db_table)
        content_type = ContentType.objects.get_for_model(obj)
        related_content_type = ContentType.objects.get_for_model(model)
        query = """
        SELECT %(model_pk)s, COUNT(related_tagged_item.object_id) AS %(count)s
        FROM %(model)s, %(tagged_item)s, %(tag)s, %(tagged_item)s related_tagged_item
        WHERE %(tagged_item)s.object_id = %%s
          AND %(tagged_item)s.content_type_id = %(content_type_id)s
          AND %(tag)s.id = %(tagged_item)s.tag_id
          AND related_tagged_item.content_type_id = %(related_content_type_id)s
          AND related_tagged_item.tag_id = %(tagged_item)s.tag_id
          AND %(model_pk)s = related_tagged_item.object_id"""
        if content_type.pk == related_content_type.pk:
            # Exclude the given instance itself if determining related
            # instances for the same model.
            query += """
          AND related_tagged_item.object_id != %(tagged_item)s.object_id"""
        query += """
        GROUP BY %(model_pk)s
        ORDER BY %(count)s DESC
        %(limit_offset)s"""
        query = query % {
            "model_pk": "%s.%s" % (model_table, qn(model._meta.pk.column)),
            "count": qn("count"),
            "model": model_table,
            "tagged_item": qn(self.model._meta.db_table),
            "tag": qn(self.model._meta.get_field("tag").rel.to._meta.db_table),
            "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 "",
        }

        cursor = connection.cursor()
        cursor.execute(query, [obj.pk])
        object_ids = [row[0] for row in cursor.fetchall()]
        if len(object_ids) > 0:
            # Use in_bulk here instead of an id__in lookup, because id__in would
            # clobber the ordering.
            object_dict = queryset.in_bulk(object_ids)
            return [object_dict[object_id] for object_id in object_ids if object_id in object_dict]
        else:
            return []
Example #14
0
    def get_related(self, obj, queryset_or_model, num=None):
        """
        Retrieve a list of instances of the specified model which share
        tags with the model instance ``obj``, ordered by the number of
        shared tags in descending order.

        If ``num`` is given, a maximum of ``num`` instances will be
        returned.
        """
        queryset, model = get_queryset_and_model(queryset_or_model)
        model_table = qn(model._meta.db_table)
        content_type = ContentType.objects.get_for_model(obj)
        related_content_type = ContentType.objects.get_for_model(model)
        query = """
        SELECT %(model_pk)s, COUNT(related_tagged_item.object_id) AS %(count)s
        FROM %(model)s, %(tagged_item)s, %(tag)s, %(tagged_item)s related_tagged_item
        WHERE %(tagged_item)s.object_id = %%s
          AND %(tagged_item)s.content_type_id = %(content_type_id)s
          AND %(tag)s.id = %(tagged_item)s.tag_id
          AND related_tagged_item.content_type_id = %(related_content_type_id)s
          AND related_tagged_item.tag_id = %(tagged_item)s.tag_id
          AND %(model_pk)s = related_tagged_item.object_id"""
        if content_type.pk == related_content_type.pk:
            # Exclude the given instance itself if determining related
            # instances for the same model.
            query += """
          AND related_tagged_item.object_id != %(tagged_item)s.object_id"""
        query += """
        GROUP BY %(model_pk)s
        ORDER BY %(count)s DESC
        %(limit_offset)s"""
        query = query % {
            'model_pk': '%s.%s' % (model_table, qn(model._meta.pk.column)),
            'count': qn('count'),
            'model': model_table,
            'tagged_item': qn(self.model._meta.db_table),
            'tag': qn(self.model._meta.get_field('tag').rel.to._meta.db_table),
            '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 '',
        }

        cursor = connection.cursor()
        cursor.execute(query, [obj.pk])
        object_ids = [row[0] for row in cursor.fetchall()]
        if len(object_ids) > 0:
            # Use in_bulk here instead of an id__in lookup, because id__in would
            # clobber the ordering.
            object_dict = queryset.in_bulk(object_ids)
            return [object_dict[object_id] for object_id in object_ids]
        else:
            return []
Example #15
0
def tagged_object_list(request,
                       queryset=None,
                       tags=None,
                       related_tags=False,
                       related_tag_counts=True,
                       union=True,
                       **kwargs):
    """
    A thin wrapper around
    ``django.views.generic.list_detail.object_list`` which creates a
    ``QuerySet`` containing instances of the given queryset or model
    tagged with the given tag.

    In addition to the context variables set up by ``object_list``, a
    ``tag`` context variable will contain the ``Tag`` instance for the
    tag.

    If ``related_tags`` is ``True``, a ``related_tags`` context variable
    will contain tags related to the given tag for the given model.
    Additionally, if ``related_tag_counts`` is ``True``, each related
    tag will have a ``count`` attribute indicating the number of items
    which have it in addition to the given tag.

    If ``union`` is ``True``, will return list of objects, which are marked
    by one of mentioned tags. In other case will return list of object, which
    are marked by all of mentioned tags.
    """
    tag_instances = get_tag_list(tags)
    if not tag_instances:
        raise Http404
    if union:
        qs_func = TaggedItem.objects.get_union_by_model
    else:
        qs_func = TaggedItem.objects.get_intersection_by_model
    if not kwargs.has_key('extra_context'):
        kwargs['extra_context'] = {}
    kwargs['extra_context']['union'] = union
    if tag_instances:
        queryset = qs_func(queryset, tag_instances)
        kwargs['extra_context']['tags'] = tag_instances
        if related_tags:
            kwargs['extra_context']['related_tags'] = \
                Tag.objects.related_for_model(tag_instances, queryset,
                                              counts=related_tag_counts)
    else:
        queryset = get_queryset_and_model(queryset)[0]
    return object_list(request, queryset, **kwargs)
Example #16
0
    def get_intersection_by_model(self,
                                  queryset_or_model,
                                  tags,
                                  wildcard=None,
                                  default_namespace=None):
        """
        Create a ``QuerySet`` containing instances of the specified
        model associated with *all* of the given list of tags.

        The ``wildcard`` and the ``default_namespace`` parameters are
        allowed. For more details see the ``get_tag_list`` function.
        """
        tags = get_tag_list(tags,
                            wildcard=wildcard,
                            default_namespace=default_namespace)
        tag_count = len(tags)
        queryset, model = get_queryset_and_model(queryset_or_model)

        if not tag_count:
            return model._default_manager.none()

        model_table = qn(model._meta.db_table)
        # This query selects the ids of all objects which have all the
        # given tags.
        query = """
        SELECT %(model_pk)s
        FROM %(model)s, %(tagged_item)s
        WHERE %(tagged_item)s.content_type_id = %(content_type_id)s
          AND %(tagged_item)s.tag_id IN (%(tag_id_placeholders)s)
          AND %(model_pk)s = %(tagged_item)s.object_id
        GROUP BY %(model_pk)s
        HAVING COUNT(%(model_pk)s) = %(tag_count)s""" % {
            'model_pk': '%s.%s' % (model_table, qn(model._meta.pk.column)),
            'model': model_table,
            'tagged_item': qn(self.model._meta.db_table),
            'content_type_id': ContentType.objects.get_for_model(model).pk,
            'tag_id_placeholders': ','.join(['%s'] * tag_count),
            'tag_count': tag_count,
        }

        cursor = connection.cursor()
        cursor.execute(query, [tag.pk for tag in tags])
        object_ids = [row[0] for row in cursor.fetchall()]
        if len(object_ids) > 0:
            return queryset.filter(pk__in=object_ids)
        else:
            return model._default_manager.none()
Example #17
0
    def get_intersection_by_model(self, queryset_or_model, tags):
        """
        Create a ``QuerySet`` containing instances of the specified
        model associated with *all* of the given list of tags.

        Because get_intersection_by_model requires an in-memory
        aggregation impelementation, it is less efficient than 
        get_union_by_model.
        """
        queryset, model = get_queryset_and_model(queryset_or_model)
        object_ids = self._get_intersection_object_ids(model, tags)

        if len(object_ids) > 1:
            return queryset.filter(pk__in=object_ids)
        elif len(object_ids) == 1:
            return queryset.filter(pk=object_ids[0])
        else:
            return model._default_manager.none()
Example #18
0
    def get_intersection_by_model(self, queryset_or_model, tags,
                                  wildcard=None, default_namespace=None):
        """
        Create a ``QuerySet`` containing instances of the specified
        model associated with *all* of the given list of tags.

        The ``wildcard`` and the ``default_namespace`` parameters are
        allowed. For more details see the ``get_tag_list`` function.
        """
        tags = get_tag_list(tags,
            wildcard=wildcard, default_namespace=default_namespace)
        tag_count = len(tags)
        queryset, model = get_queryset_and_model(queryset_or_model)

        if not tag_count:
            return model._default_manager.none()

        model_table = qn(model._meta.db_table)
        # This query selects the ids of all objects which have all the
        # given tags.
        query = """
        SELECT %(model_pk)s
        FROM %(model)s, %(tagged_item)s
        WHERE %(tagged_item)s.content_type_id = %(content_type_id)s
          AND %(tagged_item)s.tag_id IN (%(tag_id_placeholders)s)
          AND %(model_pk)s = %(tagged_item)s.object_id
        GROUP BY %(model_pk)s
        HAVING COUNT(%(model_pk)s) = %(tag_count)s""" % {
            'model_pk': '%s.%s' % (model_table, qn(model._meta.pk.column)),
            'model': model_table,
            'tagged_item': qn(self.model._meta.db_table),
            'content_type_id': ContentType.objects.get_for_model(model).pk,
            'tag_id_placeholders': ','.join(['%s'] * tag_count),
            'tag_count': tag_count,
        }

        cursor = connection.cursor()
        cursor.execute(query, [tag.pk for tag in tags])
        object_ids = [row[0] for row in cursor.fetchall()]
        if len(object_ids) > 0:
            return queryset.filter(pk__in=object_ids)
        else:
            return model._default_manager.none()
Example #19
0
def tagged_object_list(request, queryset=None, tags=None, related_tags=False,
                       related_tag_counts=True, union=True, **kwargs):
    """
    A thin wrapper around
    ``django.views.generic.list_detail.object_list`` which creates a
    ``QuerySet`` containing instances of the given queryset or model
    tagged with the given tag.

    In addition to the context variables set up by ``object_list``, a
    ``tag`` context variable will contain the ``Tag`` instance for the
    tag.

    If ``related_tags`` is ``True``, a ``related_tags`` context variable
    will contain tags related to the given tag for the given model.
    Additionally, if ``related_tag_counts`` is ``True``, each related
    tag will have a ``count`` attribute indicating the number of items
    which have it in addition to the given tag.

    If ``union`` is ``True``, will return list of objects, which are marked
    by one of mentioned tags. In other case will return list of object, which
    are marked by all of mentioned tags.
    """
    tag_instances = get_tag_list(tags)
    if not tag_instances:
        raise Http404
    if union:
        qs_func = TaggedItem.objects.get_union_by_model
    else:
        qs_func = TaggedItem.objects.get_intersection_by_model
    if not kwargs.has_key('extra_context'):
        kwargs['extra_context'] = {}
    kwargs['extra_context']['union'] = union
    if tag_instances:
        queryset = qs_func(queryset, tag_instances)
        kwargs['extra_context']['tags'] = tag_instances
        if related_tags:
            kwargs['extra_context']['related_tags'] = \
                Tag.objects.related_for_model(tag_instances, queryset,
                                              counts=related_tag_counts)
    else:
        queryset = get_queryset_and_model(queryset)[0]
    return object_list(request, queryset, **kwargs)
Example #20
0
    def get_intersection_by_model(self, queryset_or_model, tags):
        """
        Create a ``QuerySet`` containing instances of the specified
        model associated with *all* of the given list of tags.
        """
        tags = get_tag_list(tags)
        tag_count = len(tags)
        queryset, model = get_queryset_and_model(queryset_or_model)

        if not tag_count:
            return model._default_manager.none()

        model_table = qn(model._meta.db_table)
        # This query selects the ids of all objects which have all the
        # given tags.
        query = """
        SELECT %(model_pk)s
        FROM %(model)s, %(tagged_item)s
        WHERE %(tagged_item)s.content_type_id = %(content_type_id)s
          AND %(tagged_item)s.tag_id IN (%(tag_id_placeholders)s)
          AND %(model_pk)s = %(tagged_item)s.object_id
        GROUP BY %(model_pk)s
        HAVING COUNT(%(model_pk)s) = %(tag_count)s""" % {
            "model_pk": "%s.%s" % (model_table, qn(model._meta.pk.column)),
            "model": model_table,
            "tagged_item": qn(self.model._meta.db_table),
            "content_type_id": ContentType.objects.get_for_model(model).pk,
            "tag_id_placeholders": ",".join(["%s"] * tag_count),
            "tag_count": tag_count,
        }

        cursor = connection.cursor()
        cursor.execute(query, [tag.pk for tag in tags])
        object_ids = [row[0] for row in cursor.fetchall()]
        if len(object_ids) > 0:
            return queryset.filter(pk__in=object_ids)
        else:
            return model._default_manager.none()
Example #21
0
    def get_intersection_by_model(self, queryset_or_model, tags):
        """
        Create a ``QuerySet`` containing instances of the specified
        model associated with *all* of the given list of tags.
        """
        tags = get_tag_list(tags)
        tag_count = len(tags)
        queryset, model = get_queryset_and_model(queryset_or_model)

        if not tag_count:
            return model._default_manager.none()

        model_table = qn(model._meta.db_table)
        # This query selects the ids of all objects which have all the
        # given tags.
        query = """
        SELECT %(model_pk)s
        FROM %(model)s, %(tagged_item)s
        WHERE %(tagged_item)s.content_type_id = %(content_type_id)s
          AND %(tagged_item)s.tag_id IN (%(tag_id_placeholders)s)
          AND %(model_pk)s = %(tagged_item)s.object_id
        GROUP BY %(model_pk)s
        HAVING COUNT(%(model_pk)s) = %(tag_count)s""" % {
            "model_pk": "%s.%s" % (model_table, qn(model._meta.pk.column)),
            "model": model_table,
            "tagged_item": qn(self.model._meta.db_table),
            "content_type_id": ContentType.objects.get_for_model(model).pk,
            "tag_id_placeholders": ",".join(["%s"] * tag_count),
            "tag_count": tag_count,
        }

        cursor = connection.cursor()
        cursor.execute(query, [tag.pk for tag in tags])
        object_ids = [row[0] for row in cursor.fetchall()]
        if len(object_ids) > 0:
            return queryset.filter(pk__in=object_ids)
        else:
            return model._default_manager.none()
Example #22
0
    def get_intersection_by_model(self, queryset_or_model, tags):
        """
        Create a ``QuerySet`` containing instances of the specified
        model associated with *all* of the given list of tags.
        """
        tags = get_tag_list(tags)
        tag_count = len(tags)
        queryset, model = get_queryset_and_model(queryset_or_model)

        if not tag_count:
            return model._default_manager.none()

        model_table = qn(model._meta.db_table)
        tagged_item_table = qn(self.model._meta.db_table)
        # This query selects the ids of all objects which have all the
        # given tags.
        joins, params = self.get_tagged_item_joins(tags, tagged_item_table, model)
        
        query = '''
        SELECT %(model_pk)s
        FROM %(model)s
        %(joins)s
        GROUP BY %(model_pk)s
        ''' % {
            'model': model_table,
            'model_pk': '%s.%s' % (model_table, qn(model._meta.pk.column)),
            'joins': joins,
        }

        cursor = connection.cursor()
        cursor.execute(query, params)
        object_ids = [row[0] for row in cursor.fetchall()]
        if len(object_ids) > 0:
            return queryset.filter(pk__in=object_ids)
        else:
            return model._default_manager.none()