- if min_count is not None: counts = True
- tags = self.model.get_tag_list(tags)
- tag_count = len(tags)
- tagged_item_table = qn(self.intermediary_table_model._meta.db_table)
- tag_columns = self._get_tag_columns()
-
- if extra is None: extra = {}
- extra_where = ''
- if 'where' in extra:
- extra_where = 'AND ' + ' AND '.join(extra['where'])
-
- # Temporary table in this query is a hack to prevent MySQL from executing
- # inner query as dependant query (which could result in severe performance loss)
- query = """
- SELECT %(tag_columns)s%(count_sql)s
- FROM %(tagged_item)s INNER JOIN %(tag)s ON %(tagged_item)s.tag_id = %(tag)s.id
- WHERE %(tagged_item)s.content_type_id = %(content_type_id)s
- AND %(tagged_item)s.object_id IN
- (
- SELECT *
- FROM (
- SELECT %(tagged_item)s.object_id
- FROM %(tagged_item)s, %(tag)s
- WHERE %(tagged_item)s.content_type_id = %(content_type_id)s
- AND %(tag)s.id = %(tagged_item)s.tag_id
- AND %(tag)s.id IN (%(tag_id_placeholders)s)
- GROUP BY %(tagged_item)s.object_id
- HAVING COUNT(%(tagged_item)s.object_id) = %(tag_count)s
- ) AS temporary
- )
- AND %(tag)s.id NOT IN (%(tag_id_placeholders)s)
- %(extra_where)s
- GROUP BY %(tag_columns)s
- %(min_count_sql)s
- ORDER BY %(tag)s.%(ordering)s ASC""" % {
- 'tag': qn(self.model._meta.db_table),
- 'ordering': ', '.join(qn(field) for field in self.model._meta.ordering),
- 'tag_columns': tag_columns,
- 'count_sql': counts and ', COUNT(%s.object_id)' % tagged_item_table or '',
- 'tagged_item': tagged_item_table,
- 'content_type_id': ContentType.objects.get_for_model(model).pk,
- 'tag_id_placeholders': ','.join(['%s'] * tag_count),
- 'extra_where': extra_where,
- 'tag_count': tag_count,
- 'min_count_sql': min_count is not None and ('HAVING COUNT(%s.object_id) >= %%s' % tagged_item_table) or '',
- }
-
- params = [tag.pk for tag in tags] * 2
- if min_count is not None:
- params.append(min_count)
-
- cursor = connection.cursor()
- cursor.execute(query, params)
- related = []
- for row in cursor.fetchall():
- tag = self.model(*row[:len(self.model._meta.fields)])
- if counts is True:
- tag.count = row[len(self.model._meta.fields)]
- related.append(tag)
- return related
-
- def _get_tag_columns(self):
- tag_table = qn(self.model._meta.db_table)
- return ', '.join('%s.%s' % (tag_table, qn(field.column)) for field in self.model._meta.fields)