Reputation: 13016
I've got an Item
model in my Django app with a ManyToMany field that's handled through an intermediate Favorite model. Here are simplified versions of the models in question:
class Item(models.Model):
name = models.CharField(max_length=200)
class Favorite(models.Model):
user = models.ForeignKey(User)
item = models.ForeignKey(Item)
I'm trying to get a list of items ordered by the number of favorites. The query below works, however there are thousands of records in the Item table, and the query is taking up to a couple of minutes to complete.
items = Item.objects.annotate(num_favorites=Count('favorite')).order_by('-num_favorites')
Not sure if this is relevant to any potential answers, but I'm paginating the results using Django's built-in Pagintor:
paginator = Paginator(items, 100)
I know I can add a favorites
field to my Item model and increment that every time an item is favorited, but I'm wondering if there's another cleaner, more efficient way to retrieve this data in a reasonable time.
Below is the output of the MySQL EXPLAIN
function:
+----+-------------+--------------------+------+-----------------------------+-----------------------------+---------+-------------------------------+------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------------+------+-----------------------------+-----------------------------+---------+-------------------------------+------+---------------------------------+
| 1 | SIMPLE | appname_item | ALL | NULL | NULL | NULL | NULL | 566 | Using temporary; Using filesort |
| 1 | SIMPLE | appname_favorite | ref | appname_favorite_67b70d25 | appname_favorite_67b70d25 | 4 | appname.appname_item.id | 1 | |
+----+-------------+--------------------+------+-----------------------------+-----------------------------+---------+-------------------------------+------+---------------------------------+
Upvotes: 2
Views: 295
Reputation: 39689
when ever you try to use order_by clause whose index is not defined, try to defined index on it, it makes the ordering process way faster, in you case index on 'num_favorites' is what you need to speed up the query execution time. also the technique which you are using is fine there is nothing bad in it.
please see the related question which was asked by me on same issue, and saverio
gave an excellent answer to tackle the problem. Hope this helps.
Upvotes: 1
Reputation: 599866
That is already the best way. If it's slow, the problem is probably with your database - specifically, you don't have the right indexes for that query so the db is having to do too much sorting.
The Django debug toolbar is great for diagnosing this sort of thing - it will show you how long each query is taking, and allow you to run the db's EXPLAIN function on each one. The MySQL docs will tell you what the output from EXPLAIN means. However, after that it's up to you to optimise the db.
Upvotes: 1