Using handlers

As seen in Getting started, a model instance can be voted and can have an associated score only if its model class is handled. Being handled, for a model, means it is registered with an handler.

We have seen how to do that:

from ratings.handlers import ratings

The handler class is an optional argument of the ratings.register method, and, if not provided, the default ratings.handlers.RatingHandler handler is used

The previous code can be written:

from ratings.handlers import ratings, RatingHandler
ratings.register(Film, RatingHandler)

For convenience, ratings.register can also accept a list of model classes in place of a single model; this allows easier registration of multiple models with the same handler class, e.g.:

from ratings.handlers import ratings, RatingHandler
ratings.register([Film, Series], RatingHandler)

Where should this code live? You can register handlers anywhere you like. However, you’ll need to make sure that the module it’s in gets imported early on so that the model gets registered before any voting is performed or rating is requested. This makes your app’s a good place to put the above code.

The default rating handler provides only one 1-5 ranged (without decimal places) score for each content object, and allows voting only for authenticated users. It also allows user to delete and change their vote.

We can, however, override some options while registering the model. For instance, if we want 1-10 ranged votes with a step of 0.5 (half votes), and we don’t want users to delete their votes, we can give these options as kwargs:

from ratings.handlers import ratings, RatingHandler
ratings.register(Film, RatingHandler,
    score_range=(1, 10), score_step=0.5, can_delete_vote=False)

The handler manages the voting form too, and, by default the widget used to render the score is a simple text input. If you want to use the more cool star rating widget, you can do:

from ratings.handlers import ratings
from ratings.forms import StarVoteForm
ratings.register(Film, form_class=StarVoteForm)

For a list of all available built-in options, see Handlers reference.

However, there are situations where the built-in options are not sufficient.

What if, for instance, you want only active objects to be voted for a given model? As in Django own contrib.admin.ModelAdmin, you can write subclasses of RatingHandler to override the methods which actually perform the voting process, and apply any logic they desire.

Here is an example meeting the staff users needs:

from ratings.handlers import ratings, RatingHandler

class MyHandler(RatingHandler):
   def allow_vote(self, request, instance, key):
       allowed = super(MyHandler, self).allow_vote(request, instance, key)
       return allowed and instance.is_active

ratings.register(Film, MyHandler)

In the above example, the allow_vote method is called before any voting attempt, and takes the current request, the instance being voted and a key.

The key is a string representing the type of rating we are giving to an object. For example, the same film can be associated with multiple types of rating (e.g. a score for the photography, one for the direction, one for the music, and so on): a user can vote the music or the direction, so the key can be used to distinguish music from direction. In fact, the key can even be the string 'music' or the string 'direction'.

The default key is 'main'. Don’t worry: we will talk more about rating keys in Usage and examples.

Handlers API

Handlers are not only used to manage and customize the voting process, but also grant a simplified access to the underneath Django models api.

First, we have to obtain the handler instance associated with our model:

from ratings.handlers import ratings
handler = ratings.get_handler(Film)

The method ratings.get_handler returns None if model is not registered, and can take a model instance too:

from ratings.handlers import ratings
film = Film.objects.latest()
handler = ratings.get_handler(film)

What we can do with the handler? For instance, we can get the 'main' score or our film:

score = handler.get_score(film, 'main')

if score:
    print 'Average score:', score.average
    print 'Number of votes:', score.num_votes
    print 'Total score:',
    print u'Nobody voted %s' % film

Or we can check if current user has voted our film:

voted = handler.has_voted(film, 'main', request.user)

See Handlers reference for a detailed explanation of other utility methods of handlers, and of ratings.handlers.ratings registry too. And in Models reference you will find the lower level Django model’s API.

It could be clear now that the rating handler is a layer of abstraction above Django models and forms, and handlers are used by templatetags and views too. This way, building our own handlers means we can customize the behaviour of the entire application.

Before going to see the Handlers reference, maybe it is better to take a look at some Usage and examples.