django-classfaves 0.1

A different approach to favorites in Django

  Add it to your Download Basket!

 Add it to your Watch List!


Rate it!
send us
an update
BSD License 
Eric Florenzano
ROOT \ Internet \ HTTP (WWW)
django-classfaves is a Django plugin that provides everything that you need to add favoriting abilities to your site. There are just a few steps that you need to go through to add this functionality to your site.

Installation at a Glance

1.  Create a favorite model that has a ``ForeignKey`` to the domain object that you would like to be able to favorite or unfavorite.

2.  Instantiate ``CreateFavorite``, ``DeleteFavorite``, and ``UserFavorites`` in one of your url configurations, and map them into your urlpatterns.

3.  Integrate into your domain object's templates and views.

Installation Example: Arcade Site

This installation example comes from `Radiosox`_, for which this application was originally written.  The site is a free online arcade site that allows users to mark certain games as their favorite ones.  They are allowed to un-favorite those games if they change their mind.  Finally, they are allowed to see a list of their favorite games.

Step 1: Creating our Model

We already have a domain object that looks a little bit like this:

.. sourcecode:: python

    from django.db import models

    class Game(models.Model):
        name = models.CharField(max_length=64)
        slug = models.SlugField(max_length=64)
        description = models.TextField(blank=True)
        # ... there are many more fields here

Now what we want to do is in that same file, we will create our favorite model, like shown here:

.. sourcecode:: python

    from classfaves.models import FavoriteBase

    class GameFavorite(FavoriteBase):
        game = models.ForeignKey(Game)

What this does is use our ``FavoriteBase`` abstract base class, provide by django-classfaves, and ensure that it's got a link to our ``Game`` domain object. This model is equivalent to manually writing this:

.. sourcecode:: python

    import datetime

    from django.db import models
    from django.contrib.auth.models import User

    class GameFavorite(models.Model):
        game = models.ForeignKey(Game)
        user = models.ForeignKey(User)
        date_created = models.DateTimeField(

In fact, you could write that model if you prefer.  I prefer to subclass ``FavoriteBase``, since it's easier.

Step 2: Attaching our Views

In our ```` file for that arcade app whose models we were using in the previous step, we have something that, right now, looks like this:

.. sourcecode:: python

    from django.conf.urls.defaults import patterns, url

    urlpatterns = patterns('arcade.views',
        url(r'^popular/$', 'popular', name='arcade_popular'),
        url(r'^new/$', 'new', name='arcade_new'),
        # ... there are more URLs here

What we want to do first is instantiate our views provided by the django-classfaves app:

.. sourcecode:: python

    from arcade.models import GameFavorite, Game
    from classfaves.views import CreateFavorite, DeleteFavorite, UserFavorites

    create_favorite = CreateFavorite(GameFavorite, Game)
    delete_favorite = DeleteFavorite(GameFavorite, Game)
    public_games = lambda qs: qs.filter(game__enabled=True)
    user_favorites = UserFavorites(GameFavorite, Game, extra_filter=public_games)

What this does is give us views to create, delete, and get user favorites for all games in the system.  You'll note that we're passing an argument named ``extra_filter`` to ``UserFavorites`` which limits the view to only showing games with ``enabled`` set to ``True``.  This can be used to limit the ``QuerySet`` arbitrarily.  Kinda cool, huh?

Anyway, the next step is to modify our ``urlpatterns`` so that it maps to these views:

..sourcecode:: python

    urlpatterns = patterns('arcade.views',
        url(r'^popular/$', 'popular', name='arcade_popular'),
        url(r'^new/$', 'new', name='arcade_new'),
        # ... vvv THE NEW URLS ARE BELOW vvv
        url(r'^favorites/create/(?Pd+)/$', create_favorite, name='arcade_favorite_create'),
        url(r'^favorites/delete/(?Pd+)/$', delete_favorite, name='arcade_favorite_delete'),
        url(r'^favorites/list/$', user_favorites, name='arcade_my_favorites'),
        url(r'^favorites/list/(?P[a-zA-Z0-9_-]+)/$', user_favorites, name='arcade_user_favorites'),

Note how we were able to give the new views proper URL names, and how we were able to easily place them where they logically fit in the URL structure--under the URL space of the arcade app.  Also note that we've ensured to have a ``pk`` for the create and delete views.

Step 3: Integration

Admittedly, this part is the part where django-classfaves helps you out the least. Well, basically, django-classfaves doesn't help you out at all. The reason for this is by design: we don't know how your app is structured or how you want to use it, so we want to leave this bit completely up to you.

So here's how we did it. First, on the page where you actually play the game, we want you to be able to decide that you like it and favorite it. We also wanted to make sure that you could do this while you are still playing the game, so it needed to be asynchronous using JavaScript.

Our first step was to modify the view function for the game playing page.

..sourcecode:: python

    from arcade.models import GameFavorite

    def play(request, game_slug=None):
        # ... some of our view code here
        favorite = False
        if request.user.is_authenticated():
            favorite = GameFavorite.objects.filter(user=request.user,
                game=game).count() > 0
        context = {
            # ... other context here
            'favorite': favorite,
        # ... the rest of our view code here

Just a few lines of code, and we now know whether the user has a favorite on that specific domain object (the game) or not.

Now, in the template, we do this with the information:

..sourcecode:: python

    {% if user.is_authenticated %}
        {% if favorite %}
            Remove as Favorite
        {% else %}
            Add as Favorite
        {% endif %}
    {% else %}
        Add as Favorite
    {% endif %}

In other words, based on whether the user is authenticated and based on whether or not they have already favorited the game, we set some classes and urls and messages on the links.

We also have a bit of JavaScript, that looks like this:

..sourcecode:: javascript

    var add_favorite_handlers = function(base_create, base_delete) {
        $('a.favorite.fave').live('click', function(e) {
            var pk = $(this).attr('id').replace('favorite_', '');
            var url = base_create + pk + '/';
            $.getJSON(url, function(data, textStatus) {
                $('#favorite_' + pk).removeClass('fave').addClass('unfave').text('Remove as Favorite');
            return false;
        $('a.favorite.unfave').live('click', function(e) {
            var pk = $(this).attr('id').replace('favorite_', '');
            var url = base_delete + pk + '/';
            $.getJSON(url, function(data, textStatus) {
                $('#favorite_' + pk).removeClass('unfave').addClass('fave').text('Add as Favorite');
            return false;

I'm not going to go into too much detail about this JavaScript code, except to say that it takes the base URL for the create and the delete pages, and turns links with certain classes into AJAX calls into the create and delete views.

Finally, at the bottom of the page, we initialize this JavaScript like so:

.. sourcecode:: html

And with that, we're done!  Not too bad, huh?  You can browse around `Radisox` and play a game to see it in action.  Look up at the top right of any game page to see the favorite/un-favorite button.

Last updated on September 9th, 2009


#Django favorites #Django plugin #Django #favorites #faves #plugin

Add your review!