Posted on May 24, 2008 at 6:12 P.M.

Recently I launched Peevalizer, a website for talking about your pet peeves, which of course was written in Python using the Django web framework. In fact, it was the culmination of my efforts to teach myself design, and while I made some progress, it's clear that I'll never be a designer. Anyway, part of Peevalizer is that users can vote on different pet peeves, and view the peeves with the highest score. I used django-voting as the application to enable this functionality, and it provides a manager on the Vote object with methods for getting the top N results, where N is a positive integer.

One of the reasons for custom manager on Vote is because aggregate support has not yet been finished. However with Django's built-in Pagination support, it's necessary to retrieve not only a list of the top N voted pet peeves, but a list of all of the pet peeves, ordered by score. How is this possible? Specifically, how is this possible without forking django-voting? Here is the solution that I came up with:

class VoteAwareManager(models.Manager):
    def _get_score_annotation(self):
        model_type = ContentType.objects.get_for_model(self.model)
        table_name = self.model._meta.db_table
        return self.extra(select={
            'score': 'SELECT COALESCE(SUM(vote),0) FROM %s WHERE content_type_id=%d AND object_id=%s.id' %
                (Vote._meta.db_table, int(model_type.id), table_name)}
        )

    def most_hated(self):
        return self._get_score_annotation().order_by('-score')

    def most_loved(self):
        return self._get_score_annotation().order_by('score')

Then I assigned that manager onto all of the objects that could be voted on. What that's doing is literally issuing a subquery for every row, doing an aggregate on all of the votes for that row, and assigning it to an attribute named score.

However, we also wanted to allow for voting on User objects, which is built in to Django and cannot be easily changed. How do we add this manager to user? I spent a while thinking about that before realizing that it's not the right question to ask. The right question to ask is, how can we associate the User model with this manager? A quick look through some Django source code revealed this to be an absolutely trivial task. Here's how it goes in our code:

from django.contrib.auth.models import User
manager = VoteAwareManager()
manager.model = User

for user in manager.most_hated():
    # Do something with user's score

There are a few things to note about this implementation. Firstly, it can be much more computationally expensive to use this method instead of using django-voting's method (which executes some custom SQL), so either be aware of that or use aggressive caching strategies to overcome this shortcoming. The other thing is if you're not using a manager like this on multiple models, and since managers mostly just proxy to QuerySet anyway, it might be simpler to just acquire a QuerySet on the model that you would like to get, and run the extra() method in the calling function.

at 8:13 p.m.
on May 24, 2008

Hey Eric, thanks for the tips on voting. We just launched <a href="http://www.myyardourmessage.com/">My Yard Our Message</a>, also built on django and will soon incorporate voting (though not there yet). I've been planning on django voting as well, though was kind of wondering how getting a score out of it would work. This is helpful.

Btw.. on design stuff as it pertains to Peevalizer, more line-spacing, more padding, and justify the sidebar to the left. Right justified text doesn't make visual sense on the right side of the page.

Dev Mem
at 9:11 a.m.
on May 27, 2008

Cool.

BTW, how do you colorize your HTML Python output?

at 4:13 a.m.
on June 23, 2008

PzcfTX dfv078fnw8f934ndvkg2l

Search

 

Recent Links

  • Callcast - Discussion with Jeff Croft
  • Great discussion with Jeff Croft and Kevin Fricovsky, talking about Django, design, web standards, and various other things. Kevin has really been on fire lately in his blog, and Jeff has some good stuff to say. Both their sites are bookmarks, for sure.

  • git-issues: A distributed issue tracker built-in to Git.
  • I predicted this back in March--can't believe a solution has surfaced so soon. It makes so much sense to build in an issue tracker to a revision control system. Since you're working with the code, might as well track the issues in the same system and take advantage of the extra metadata. This is really cool (and as a bonus, it's written in Python) so I hope to see it really grow and flourish!

  • How I Work Daily
  • Daily blog by Kevin Fricovsky. In addition to having some really great content, he has started to post audio interviews with people from the Django community. This is a site to keep an eye on in the coming days and months.

  • django-arcade
  • Demo site for django-arcade, an open source reusable Django app to add new flash games to any django-powered site. Looks very cool for easily creating game portals. It also comes from my future employer.

  • Facebook Chat and Scalability (with Erlang)
  • Eugene Letuchy talks about how they they took Facebook Chat from no users to 70 million users, with the help of Erlang.

  • Simon Willison: The Implications of OpenID
  • I somehow missed this presentation when it came out, but it's an absolutely fantastic overview and defense of OpenID by Simon Willison. If you are in any way interested in what OpenID is and what it can offer, you owe it to yourself to check out this presentation.

  • See the rest of my links...

Pownce

Badges

  • django badge
  • apache badge
  • GeoURL
  • XFN Friendly
  • Valid HTML 4.01 Transitional