def top_items(self, item_type, num=5): """ Returns a list of the top ``num`` objects of a particular type, based on the number of Snippets associated with them; for example, with ``item_type=tag``, returns a list of Tags based on how many Snippets they've been used for. Acceptable values for ``item_type`` are: * 'author' -- will return the users who have submitted the most Snippets. * 'language' -- will return the most-used languages. * 'tag' -- will return the most-used tags. """ # Need all of these up-front so the mapping dictionary can be built # correctly. from django.contrib.auth.models import User from models import Language, Tag, Snippet # ``_meta`` strikes again. object_mapping = { 'author': {'model': User, 'primary_table': User._meta.db_table, 'secondary_table': Snippet._meta.db_table }, 'tag': { 'model': Tag, 'primary_table': Tag._meta.db_table, 'secondary_table': Snippet._meta.get_field('tags').m2m_db_table() }, 'language': {'model': Language, 'primary_table': Language._meta.db_table, 'secondary_table': Snippet._meta.db_table }, } params = object_mapping[item_type] query = """SELECT p.id AS object_id, COUNT(*) AS score FROM %s p INNER JOIN %s s WHERE p.id = s.%s GROUP BY object_id ORDER BY score DESC""" % (params['primary_table'], params['secondary_table'], item_type + '_id') from django.db import connection cursor = connection.cursor() cursor.execute(query, []) object_ids = [row[0] for row in cursor.fetchall()[:num]] # Use ``in_bulk`` here instead of an ``id__in`` lookup, because ``id__in`` # would clobber the ordering. object_dict = params['model']._default_manager.in_bulk(object_ids) return [object_dict[object_id] for object_id in object_ids]