1. Josh VanderLinden
  2. django-bibliophile

Source

django-bibliophile / bibliophile / templatetags / bibliophile_tags.py

from django import template
from bibliophile.models import Book, Progress

register = template.Library()

class BooksNode(template.Node):
    """
    Injects a list of finished/unfinished/planned books into the context for 
    the user to render.
    
    TODO: Make this work on a site with multiple users.
    """
    def __init__(self, count, varname, finished=False, planned=False):
        self.count = int(count)
        self.varname = varname
        self.finished = finished
        self.planned = planned
    
    def render(self, context):
        """
        Determines which books should be returned.
        """

        if self.planned:
            # books planned to read
            qs = Book.objects.order_by('?')
            books = [b for b in qs if b.progress_set.count() == 0]
            context[self.varname] = books[:self.count]
            return ''

        if self.finished:
            # finished books
            op = '>='
        else:
            # books that have been started but not finished
            op = '<'
        
        params = ("bibliophile_book.id = book_id",
                  "milestone %s bibliophile_book.maximum_progress" % op)
        
        qs = Progress.objects.extra(where=params)
        books = []
        for progress in qs:
            if progress.book not in books:
                books.append(progress.book)
                
                if len(books) >= self.count: break

        context[self.varname] = books
        return ''

def finished_books(parser, token):
    try:
        tag, count, a, varname = token.split_contents()
    except ValueError:
        raise template.TemplateSyntaxError('finished_books syntax: {% finished_books 5 as finished %}')
    
    return BooksNode(count, varname, finished=True)

def unfinished_books(parser, token):
    try:
        tag, count, a, varname = token.split_contents()
    except ValueError:
        raise template.TemplateSyntaxError('unfinished_books syntax: {% unfinished_books 5 as unfinished %}')
    
    return BooksNode(count, varname, finished=False)

def planning_to_read(parser, token):
    try:
        tag, count, a, varname = token.split_contents()
    except ValueError:
        raise template.TemplateSyntaxError('planning_to_read syntax: {% planning_to_read 5 as planned %}')
    
    return BooksNode(count, varname, planned=True)

register.tag(finished_books)
register.tag(unfinished_books)
register.tag(planning_to_read)