Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Pluggable rating fields in Django.

branch: master

This branch is 0 commits ahead and 0 commits behind master

Fetching latest commit…

Cannot retrieve the latest commit at this time

README.rst

django-ratings

A generic ratings module. The field itself appends two additional fields on the model, for optimization reasons. It adds <field>_score, and <field>_votes fields, which are both integer fields.

This fork extends upon David Cramer's django-ratings in a way to make ratings generic to better support and adapt to different rating systems, such as votings, star ratings, like/dislike, flags and others.

Installation

You will need to add djangoratings to your INSTALLED_APPS:

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'djangoratings',
)

Finally, run python manage.py syncdb in your application's directory to create the tables.

Setup your models

The way django-ratings is built requires you to attach a RatingField to your models. This field will create two columns, a votes column, and a score column. They will both be prefixed with your field name:

from djangoratings.fields import RatingField

class MyModel(models.Model):
    rating = RatingField(range=5) # 5 possible rating values, 1-5

Alternatively you could do something like:

from djangoratings.fields import AnonymousRatingField

class MyModel(models.Model):
    rating = AnonymousRatingField(range=10)

If you'd like to use the built-in weighting methods, to make it appear more difficult for an object to obtain a higher rating, you can use the weight kwarg:

class MyModel(models.Model):
    rating = RatingField(range=10, weight=10)

RatingField allows the following options:

  • lower = 0 - The lower value in the range for which values are accepted. For example, a lower value of -1, says the voting scores start at -1.
  • upper = 2 - The upper value in the range for which values are accepted. For example, an upper value of 1, says the highest possible voting score is 1.
  • range = 2 - Upper and range are synonymous. For example, a range of 2, says there are 2 possible vote scores (provided lower is not set, or is 0).
  • can_change_vote = False - Allow the modification of votes that have already been made.
  • allow_delete = False - Allow the deletion of existent votes. Works only if can_change_vote = True
  • allow_anonymous = False - Whether to allow anonymous votes.
  • use_cookies = False - Use COOKIES to authenticate user votes. Works only if allow_anonymous = True.
  • values = [lower ... upper] - List of strings accepted as alternative to score integer values. For example, ['clear', 'favorite'] would make it so the voting system accepts eigher 'clear' or 'favorite' in addition to 0 and 1, respectively.
  • titles = [] - List of strings used to have verbose names the voting scores. For example, [_("Clear"), _("Favorite")].
  • widget_template = 'djangoratings/_rating.html' - Accepts the template name used to display the widget.

Also available there are VotingField, FavoriteField and FlagField, with their anonymous alternatives:

from djangoratings.fields import VotingField

class MyModel(models.Model):
    rating = VotingField() # accepting 'down', 'clear' and 'up'

VotingField's default options are:

  • lower = -1
  • upper = 1
  • values = ['down', 'clear', 'up']
  • titles = [_("Down"), _("Clear"), _("Up")]
  • widget_template = 'djangoratings/_voting.html'

FavoriteField's default options are:

  • lower = 0
  • upper = 1
  • values = ['clear', 'favorite']
  • titles = [_("Clear"), _("Favorite")]
  • widget_template = 'djangoratings/_favorite.html'

FlagField's default options are:

  • lower = 0
  • upper = 1
  • values = ['clear', 'flag']
  • titles = [_("Clear"), _("Flag")]
  • widget_template = 'djangoratings/_flag.html'

Using the model API

And adding votes is also simple:

myinstance.rating.add(score=1, user=request.user, ip_address=request.META['REMOTE_ADDR'], request.COOKIES) # last param is optional - only if you use COOKIES-auth

Retrieving votes is just as easy:

myinstance.rating.get_rating_for_user(request.user, request.META['REMOTE_ADDR'], request.COOKIES) # last param is optional - only if you use COOKIES-auth

New You're also able to delete existent votes (if deletion enabled):

myinstance.rating.delete(request.user, request.META['REMOTE_ADDR'], request.COOKIES) # last param is optional - only if you use COOKIES-auth

Accessing information about the rating of an object is also easy:

# these do not hit the database
myinstance.rating.votes
myinstance.rating.score

How you can order by top-rated using an algorithm (example from Nibbits.com source):

# In this example, ``rating`` is the attribute name for your ``RatingField``
qs = qs.extra(select={
    'rating': '((100/%s*rating_score/(rating_votes+%s))+100)/2' % (MyModel.rating.range, MyModel.rating.weight)
})
qs = qs.order_by('-rating')

Get overall rating for your instance on a scale [0-range]:

myinstance.rating.get_rating()

Get recent ratings for your instance:

# This returns ``Vote`` instances.
myinstance.rating.get_ratings()[0:5]

Get the percent of voters approval:

myinstance.rating.get_percent()

Get that same percentage, but excluding your weight:

myinstance.rating.get_real_percent()

Generic Views: Processing Votes

The best way to use the generic views is by extending it, or calling it within your own code:

from djangoratings.views import AddRatingFromModel

urlpatterns = patterns('',
    url(r'rate-my-post/(?P<object_id>\d+)/(?P<score>\d+)/', AddRatingFromModel(), {
        'app_label': 'blogs',
        'model': 'post',
        'field_name': 'rating',
    }),
)

Another example, on Nibbits we use a basic API interface, and we simply call the AddRatingView within our own view:

from djangoratings.views import AddRatingView

# For the sake of this actually looking like documentation:
params = {
    'content_type_id': 23,
    'object_id': 34,
    'field_name': 'ratings', # this should match the field name defined in your model
    'score': 1, # the score value they're sending
}
response = AddRatingView()(request, **params)
if response.status_code == 200:
    if response.content == 'Vote recorded.':
        request.user.add_xp(settings.XP_BONUSES['submit-rating'])
    return {'message': response.content, 'score': params['score']}
return {'error': 9, 'message': response.content}

COOKIE format

New: For now COOKIE name has fixed format: "vote-{{ content_type.id }}.{{ object.id }}.{{ rating_field.key }}[:6]" and COOKIE value is simple datetime-stamp.

Example: vote-15.56.2c5504=20101213101523456000

And this COOKIE lives in user's browser for 1 year (this period is also fixed for now)

This feature may change in the future

Limit Votes Per IP Address

New in 0.3.5: There is now a setting, RATINGS_VOTES_PER_IP, to limit the number of unique IPs per object/rating-field combination. This is useful if you have issues with users registering multiple accounts to vote on a single object:

RATINGS_VOTES_PER_IP = 3

Template Tags

Right now django-ratings has limited support for template tags, and only for Django.

rating_by_request

Retrieves the Vote cast by a user on a particular object and stores it in a context variable. If the user has not voted, the context variable will be 0:

{% rating_by_request request on instance.field as vote %}

If you are using Coffin, a better approach might be:

{% with instance.field_name.get_rating_for_user(request.user, request.META['REMOTE_ADDR'], request.COOKIES) as vote %}
        Do some magic with {{ vote }}
{% endwith %}

To use the request context variable you will need to add django.core.context_processors.request to the TEMPLATE_CONTEXT_PROCESSORS setting.

rating_by_user

It is recommended that you use rating_by_request as you will gain full support for anonymous users if they are enabled

Retrieves the Vote cast by a user on a particular object and stores it in a context variable. If the user has not voted, the context variable will be 0:

{% rating_by_user user on instance.field as vote %}

rating_widget

Uses widget_template passed to the field to render the rating field widget:

{% rating_widget on instance.field %}

If you want to use a different widget_template, pass the template name as:

{% rating_widget on instance.field using "template_name.html" %}

The context is passed to the template and additionally, the template receives:

  • content_type - The content type of the instance object.

  • instance - The object instance.

  • model - The model name for the object.

  • app_label - The app label for the object.

  • object_id - The object instance ID.

  • field_name - The field name.

  • had_voted - If the user has voted previously, the voted score.

  • votes - Number of total votes.

  • score - The overall voting score for the object.

  • vote - The overall voting score for the object, as an integer.

  • percent - The overall voting score for the object, as a percentage.

  • real_percent - The overall voting score for the object, as a percentage (without taking into account the weights).

  • positive - Number of positive votes (when applicable).

  • negative - Number of negative votes (when applicable).

  • ratings - a list of checked, value and title. For example:

    [
        { 'checked': False, 'value': 'clear', 'title: 'Clear' },
        { 'checked': True, 'value': 'favorite', 'title: 'Favorite' },
    ]
    
Something went wrong with that request. Please try again.