* Added pagination
[django-tables2.git] / django_tables / templatetags / django_tables.py
1 """
2 Allows setting/changing/removing of chosen url query string parameters, while
3 maintaining any existing others.
4
5 Expects the current request to be available in the context as ``request``.
6
7 Examples:
8
9     {% set_url_param page=next_page %}
10     {% set_url_param page="" %}
11     {% set_url_param filter="books" page=1 %}
12
13 """
14 import urllib
15 import tokenize
16 import StringIO
17 from django import template
18 from django.template.loader import get_template
19 from django.utils.safestring import mark_safe
20
21
22 register = template.Library()
23
24
25 class SetUrlParamNode(template.Node):
26     def __init__(self, changes):
27         self.changes = changes
28
29     def render(self, context):
30         request = context.get('request', None)
31         if not request:
32             return ""
33         # Note that we want params to **not** be a ``QueryDict`` (thus we
34         # don't use it's ``copy()`` method), as it would force all values
35         # to be unicode, and ``urllib.urlencode`` can't handle that.
36         params = dict(request.GET)
37         for key, newvalue in self.changes.items():
38             newvalue = newvalue.resolve(context)
39             if newvalue == '' or newvalue is None:
40                 params.pop(key, False)
41             else:
42                 params[key] = unicode(newvalue)
43
44         # ``urlencode`` chokes on unicode input, so convert everything to utf8.
45         # Note that if some query arguments passed to the site have their
46         # non-ascii characters screwed up when passed though this, it's most
47         # likely not our fault. Django (the ``QueryDict`` class to be exact)
48         # uses your projects DEFAULT_CHARSET to decode incoming query strings,
49         # whereas your browser might encode the url differently. For example,
50         # typing "ä" in my German Firefox's (v2) address bar results in "%E4"
51         # being passed to the server (in iso-8859-1), but Django might expect
52         # utf-8, where ä would be "%C3%A4"
53         def mkstr(s):
54             if isinstance(s, list):
55                 return map(mkstr, s)
56             else:
57                 return s.encode('utf-8') if isinstance(s, unicode) else s
58         params = dict([(mkstr(k), mkstr(v)) for k, v in params.items()])
59         # done, return (string is already safe)
60         return '?' + urllib.urlencode(params, doseq=True)
61
62
63 @register.tag
64 def set_url_param(parser, token):
65     bits = token.contents.split()
66     qschanges = {}
67     for i in bits[1:]:
68         try:
69             a, b = i.split('=', 1)
70             a = a.strip()
71             b = b.strip()
72             a_line_iter = StringIO.StringIO(a).readline
73             keys = list(tokenize.generate_tokens(a_line_iter))
74             if keys[0][0] == tokenize.NAME:
75                 # workaround bug #5270
76                 b = (template.Variable(b) if b == '""' else
77                      parser.compile_filter(b))
78                 qschanges[str(a)] = b
79             else:
80                 raise ValueError
81         except ValueError:
82             raise (template.TemplateSyntaxError,
83                    "Argument syntax wrong: should be key=value")
84     return SetUrlParamNode(qschanges)
85
86
87 class RenderTableNode(template.Node):
88     def __init__(self, table_var_name):
89         self.table_var = template.Variable(table_var_name)
90
91     def render(self, context):
92         table = self.table_var.resolve(context)
93         request = context.get('request', None)
94         context = template.Context({'request': request, 'table': table})
95         try:
96             table.request = request
97             return get_template('django_tables/table.html').render(context)
98         finally:
99             pass
100             #del table.request
101
102
103 @register.tag
104 def render_table(parser, token):
105     try:
106         _, table_var_name = token.contents.split()
107     except ValueError:
108         raise template.TemplateSyntaxError,\
109           "%r tag requires a single argument" % token.contents.split()[0]
110     return RenderTableNode(table_var_name)