Using Django to calculate "most popular tags", or any query that makes use of an aggregation function, is not the most intuitive process. I decided to roll my own Tags system, having already implemented one in PHP. Let's say I'm working on a simple blog -- here are a couple models:
class Entry(models.Model):
title = models.CharField(max_length=255)
body = models.TextField()
pub_date = models.DateTimeField(editable=False)
class Tag(models.Model):
name = models.CharField(max_length=50)
entry = models.ForeignKey(Entry)
objects = TagManager()
So, the query to pull the most popular tags would look like this:
SELECT name, COUNT(name) AS count
FROM tags
GROUP BY name
ORDER BY count DESC, name ASC
Getting this query into the ORM gave me a bit of a headache, but using Django's aggregation features makes it not too difficult. The TagManager will handle grabbing the most popular tags. Here is what it looks like:
class TagManager(models.Manager):
def top_tags(self, max=15):
top = self.model.objects.values('name').annotate(score=Count('entry')).order_by('-score')[:max]
return [tag['name'] for tag in top]
Note the list comprehension that occurs after the query. This is because performing just the first line (which annotates the queryset) will return a ValuesQuerySet object -- which looks like:
[{'score': 3, 'name': u'python'},
{'score': 1, 'name': u'bbcode'},
{'score': 1, 'name': u'blog'},
{'score': 1, 'name': u'django'}]
Since I just want the most popular tags, I'll run through the result set, grabbing the names and creating a new list. If I want to find entries that match a certain tag:
class EntryManager(models.Manager):
def matches_tag(self, tag):
return self.model.objects.filter(tag__name__iexact=tag)
Comments (0)
Commenting has been disabled for this entry
