とりあえず、な generic view の object_list の拡張
前回の続き。相変わらず情報収集をサボっていてあんまりよく知らないので自分で作るところからスタート。いいフレームワークっていうのは、調べるより自分で作った方が早いと思わせてくれるなあ。かといってそこで作っちゃ負けだけど。
誰も使わないとは思うけど、filter とか inclusion_tag とか simple_tag では実現できない複雑な templatetags の作り方とか参考になるといいなと思って晒してみる。
ちなみに、これは admin のテンプレートや CSS を流用して手軽に書けるようにするもの。SVN の HEAD ではもっと楽になってるのかなあ。
views.generic.py:
from django.http import HttpResponse from django.views.generic.list_detail import object_list as _object_list def object_list(request, queryset = None, *args, **kwargs): # queryset は ``Model.objects.all()`` みたいに直接指定する代わりに # ``lambda req: Model.objects.all()`` のように指定する。 # 複雑な事をやろうとすると urlpatterns が長くなるのが問題になりそうだけど。 qs = queryset(request) if request.GET.has_key('sort_by'): qs = qs.order_by(*request.GET.getlist('sort_by')) return _object_list(request, queryset = qs, *args, **kwargs)
templatetags/listutils.py:
from django.utils.html import escape from django.utils.translation import gettext from django.template import Library, Node, TemplateSyntaxError, TokenParser, resolve_variable register = Library() class SortableListHeaderTokenParser(TokenParser): def top(self): values = [] while True: values.append(self.value()) if not self.more(): break return values class SortableListHeaderNode(Node): class RenderHelper: def __init__(self, queryset): sorted_fields = {} # XXX: 本来は _order_by はのぞいちゃダメと思われる if hasattr(queryset._order_by, '__iter__'): for spec in queryset._order_by: if spec[0] == '-': sorted_fields[spec[1:]] = -1 else: sorted_fields[spec] = 1 self._sorted_fields = sorted_fields self._queryset = queryset def render(self, name): field_verbose_name = gettext( self._queryset.model._meta.get_field(name).verbose_name) retval = '' spec = self._sorted_fields.get(name, None) if spec != None: retval += '<th class="sorted %s">' % ( spec > 0 and 'ascending' or 'descending', ) else: retval += '<th>' retval += '<a href="?sort_by=%s%s">%s</a>' % ( (self._sorted_fields.get(name, -1) > 0 and '-' or ''), name, escape(field_verbose_name), ) retval += '</th>' return retval def __init__(self, queryset, *fields): self.queryset = queryset self.fields = fields def render_header_column(self, queryset, name): return retval def render(self, context): queryset = resolve_variable(self.queryset, context) rdr = self.RenderHelper(queryset) return ''.join([ rdr.render(resolve_variable(field, context)) for field in self.fields ]) def sortable_list_header(parser, token): args = SortableListHeaderTokenParser(token.contents).top() return SortableListHeaderNode(*args) register.tag('sortable_list_header', sortable_list_header)
テンプレートの例 (admin のテンプレートを流用)
{% extends "main/base_site.html" %} {% load listutils i18n %} {% block content %}<div id="content-main"> {% block object-tools %} <ul class="object-tools"> <li><a href="+" class="addlink">{% trans 'Add a new project' %}</a></li> </ul> {% endblock %} <table> <thead> <tr> {% sortable_list_header object_list 'id' 'name' %} </tr> </thead> <tbody> {% for object in object_list %} <tr> <td><a href="{{ object.get_absolute_url }}">{{ object.id }}</a></td> <td><a href="{{ object.get_absolute_url }}">{{ object.name }}</a></td> </tr> {% endfor %} </tbody> </table> </div>{% endblock %}