You've probably been in a situation where you need to generate a bunch of links, all of which have only slight variations in get parameters. maybe for pagination, or some advanced list filters. Today we're going to make a template tag that will generate a querystring from a dict... Now hold on.. There's already a QueryDict class which has a .urlencode() method which we can use right? Well, that doesn't give us any flexibility to swap out variables in the querystring. This is probably best demonstrated with an example.
Let's say for example you have a dict in your template context called 'filters'
filters = {'page': 1, 'name__istartswith': 'a'}
now, say we want to generate a series of page links which will also include the name__istartswith argument (as well as any others that might be in that dict). Assuming we have another context variable called 'pages' which contains a list of page numbers, we can simply do this in our template.
{% for p in pages %}
<a href="{% qs filters %}page={{ p }}{% endqs %}">{{ p }}</a>
{% endfor %}
This would generate the following html code
<a hef="?page=1&name__istartswith=a">1</a> <a hef="?page=2&name__istartswith=a">2</a> <a hef="?page=3&name__istartswith=a">3</a>
You can pass in multiple dicts to the tag which will be merged. You can also re-assign as many variables as you like, unset variables, or add new ones. here is a more advanced example
<!-- assuming params = {'other': 100} --> <a href="{% qs filters params %}page=&name__istartswith=b&sort=1{% endqs %}">View All</a> <!-- produces --> <a href="?name__istartswith=b&sort=1&other=100">View All</a>
This would be even cooler if it was able to support multiple values like a real QueryDict. The problem then becomes how do you determine whether you are adding a value, or changing the value of a variable. If anyone has any thoughts on that, please let me know.
The full code for the template tag is below. Note that if a value in the dict is a boolean, it is converted to 1 or 0 before being put into the querystring.
from django import template from django.http import QueryDict register = template.Library() def querystring(qsdict, extra=None): qs = QueryDict("").copy() qs.update(qsdict) if extra: for bit in extra.split("&"): k, v = bit.split("=") if v: qs[k] = v elif k in qs: del(qs[k]) for k in qs.keys(): if isinstance(qs[k], bool): qs[k] = 1 if qs[k] else 0 elif qs[k] is None: del(qs[k]) return qs.urlencode() @register.tag('qs') def do_qs(parser, token): nodelist = parser.parse(('endqs',)) parser.delete_first_token() dicts = token.split_contents() del(dicts[0]) return QSNode(dicts, nodelist) class QSNode(template.Node): def __init__(self, qdicts, nodelist): self.nodelist = nodelist self.qdicts = [template.Variable(qdict) for qdict in qdicts] def render(self, context): qdicts = [qdict.resolve(context) for qdict in self.qdicts] qdict = qdicts.pop(0) while qdicts: qdict.update(qdicts.pop(0)) inner = self.nodelist.render(context).strip() qs = querystring(qdict, extra=inner) if qs: qs = "?" + qs return qs
RSS