Skip to content

Instantly share code, notes, and snippets.

@camilonova
Created June 15, 2010 20:33
Show Gist options
  • Save camilonova/439688 to your computer and use it in GitHub Desktop.
Save camilonova/439688 to your computer and use it in GitHub Desktop.
from django import template
from django.http import Http404
from django.conf import settings
register = template.Library()
DEFAULT_SORT_UP = getattr(settings, 'DEFAULT_SORT_UP' , '↑')
DEFAULT_SORT_DOWN = getattr(settings, 'DEFAULT_SORT_DOWN' , '↓')
INVALID_FIELD_RAISES_404 = getattr(settings,
'SORTING_INVALID_FIELD_RAISES_404' , False)
sort_directions = {
'asc': {'icon':DEFAULT_SORT_UP, 'inverse': 'desc'},
'desc': {'icon':DEFAULT_SORT_DOWN, 'inverse': 'asc'},
'': {'icon':DEFAULT_SORT_DOWN, 'inverse': 'asc'},
}
def anchor(parser, token):
"""
Parses a tag that's supposed to be in this format: {% anchor field title %}
"""
bits = [b.strip('"\'') for b in token.split_contents()]
if len(bits) < 2:
raise template.TemplateSyntaxError, "anchor tag takes at least 1 argument"
try:
title = bits[2]
except IndexError:
title = bits[1].capitalize()
return SortAnchorNode(bits[1].strip(), title.strip())
class SortAnchorNode(template.Node):
"""
Renders an <a> HTML tag with a link which href attribute
includes the field on which we sort and the direction.
and adds an up or down arrow if the field is the one
currently being sorted on.
Eg.
{% anchor name Name %} generates
<a href="/the/current/path/?sort=name" title="Name">Name</a>
"""
def __init__(self, field, title):
self.field = field
self.title = title
def render(self, context):
request = context['request']
getvars = request.GET.copy()
if 'sort' in getvars:
sortby = getvars['sort']
del getvars['sort']
else:
sortby = ''
if 'dir' in getvars:
sortdir = getvars['dir']
del getvars['dir']
else:
sortdir = ''
if sortby == self.field:
getvars['dir'] = sort_directions[sortdir]['inverse']
icon = sort_directions[sortdir]['icon']
else:
icon = ''
if len(getvars.keys()) > 0:
urlappend = "&%s" % getvars.urlencode()
else:
urlappend = ''
if icon:
title = "%s %s" % (self.title, icon)
else:
title = self.title
url = '%s?sort=%s%s' % (request.path, self.field, urlappend)
return '<a href="%s" title="%s">%s</a>' % (url, self.title, title)
def autosort(parser, token):
bits = token.split_contents()
if len(bits) not in (2, 4):
raise template.TemplateSyntaxError, "autosort tag takes exactly one argument"
try:
if bits[2] != 'as':
raise template.TemplateSyntaxError(
"Context variable assignment must take the form of {%% %s"
" queryset as context_var_name %%}" % bits[0]
)
except IndexError:
pass
try:
return SortedDataNode(bits[1], bits[-1])
except IndexError:
return SortedDataNode(bits[1])
class SortedDataNode(template.Node):
"""
Automatically sort a queryset with {% autosort queryset %}
"""
def __init__(self, queryset_var, context_var=None):
self.queryset_var = template.Variable(queryset_var)
self.context_var = context_var
def render(self, context):
if self.context_var is None:
key = self.queryset_var.var
else:
key = self.context_var
value = self.queryset_var.resolve(context)
order_by = context['request'].field
if len(order_by) > 1:
try:
context[key] = value.order_by(order_by)
# Test if the queryset can be executed
context[key].all().exist()
except:
if INVALID_FIELD_RAISES_404:
raise Http404('Invalid field sorting. If DEBUG were set to ' +
'False, an HTTP 404 page would have been shown instead.')
context[key] = value
else:
context[key] = value
return ''
anchor = register.tag(anchor)
autosort = register.tag(autosort)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment