Displaying stars (with rounding) as a Django templatetag
Since my last post was quite popular (by my simple blogging standards), I’ve decided to post another Django snippet that I’ve used while coding WeGoEat. (I know, I know… one of these days I’ll finish. I’ve just been really, really lazy busy.)
After reading Leah’s blog (of Pownce fame) about “rounding to the nearest half”, I was immediately struck by two sobering realities:
- I would need that exact same algorithm for my current site.
- A few years out of grad school, and I couldn’t even begin to fathom a solution. Thank you, Internet.
In any case, I figured I’d wrap my solution into a template tag that people could use/steal/copy. Here’s a generated test page that illustrates what my template tag would display.

You can even customize the display a bit:

I know, I know… I can do a lot better.
In any case, here’s the code snippet that I placed in a file called show_stars.py:
import math from django.template import Library, Node, TemplateSyntaxError, VariableDoesNotExist, resolve_variable from django.conf import settings register = Library() IMG_TEMPLATE = '<img src="%s" alt="%s"/>' PATH_TO_WHOLE_STAR = IMG_TEMPLATE % (settings.MEDIA_URL + 'img/stars/star.png', "Whole Star") PATH_TO_THREE_QUARTER_STAR = IMG_TEMPLATE % (settings.MEDIA_URL + 'img/stars/three-quarter.png', "3/4 Star") PATH_TO_HALF_STAR = IMG_TEMPLATE % (settings.MEDIA_URL + 'img/stars/half.png', "1/2 Star") PATH_TO_QUARTER_STAR = IMG_TEMPLATE % (settings.MEDIA_URL + 'img/stars/quarter.png', "1/4 Star") PATH_TO_BLANK_STAR = IMG_TEMPLATE % (settings.MEDIA_URL + 'img/stars/blank.png', "Empty Star") class ShowStarsNode(Node): """ Default rounding is to the whole unit """ def __init__(self, context_var, total_stars, round_to): self.context_var = context_var self.total_stars = int(total_stars) self.round_to = round_to.lower() def render(self, context): try: stars = resolve_variable(self.context_var, context) except VariableDoesNotExist: return '' if self.round_to == "half": stars = round(stars*2)/2 elif self.round_to == "quarter": stars = round(stars*4)/4 else: stars = round(stars) fraction, integer = math.modf(stars) integer = int(integer) output = [] for whole_star in range(integer): output.append(PATH_TO_WHOLE_STAR) if self.round_to == 'half' and fraction == .5: output.append(PATH_TO_HALF_STAR) elif self.round_to == 'quarter': if fraction == .25: output.append(PATH_TO_QUARTER_STAR) elif fraction == .5: output.append(PATH_TO_HALF_STAR) elif fraction == .75: output.append(PATH_TO_THREE_QUARTER_STAR) if fraction: integer += 1 blanks = int(self.total_stars - integer) for blank_star in range(blanks): output.append(PATH_TO_BLANK_STAR) return "".join(output) """ show_stars context_var of 5 round to half """ def do_show_stars(parser, token): args = token.contents.split() if len(args) != 7: raise TemplateSyntaxError('%s tag requires exactly six arguments' % args[0]) if args[2] != 'of': raise TemplateSyntaxError("second argument to '%s' tag must be 'of'" % args[0]) if args[4] != 'round': raise TemplateSyntaxError("fourth argument to '%s' tag must be 'round'" % args[0]) if args[5] != 'to': raise TemplateSyntaxError("fourth argument to '%s' tag must be 'to'" % args[0]) return ShowStarsNode(args[1], args[3], args[6]) register.tag('show_stars', do_show_stars)
I’ve also wrapped up the star graphics I used. A big ups to the Silk Icons creator.
For those of you that don’t care for .zip files, here’s all the star images I used:





To use the tag, it’ll look something like this in your templates:
{% load show_stars %} {% show_stars context_var of 5 round to half %} {% endif %}
Note: You can change the following:
- context_var is whatever var contains the value you want rounded (it was ‘value’ in the screenshots)
- 5 can be any integer; this is the maximum number of stars to display. I should probably check to make sure you don’t put a number smaller than context_var, but I’ll leave that up to you.
- half can be any of the following values: whole, half, or quarter
- Make sure to edit the paths to the star files in the templatetag file
And since this is on my blog, feel totally free to take/use/steal/distribute/copy/modify any code you see fit. All I ask is if you find any bugs, have any comments, or can think of ways to make my ugly code cleaner – I’d love to hear from you.
Enjoy!
Popularity: 36% [?]


