# app/templatetags/basic.py
from django import template
from django.utils.encoding import smart_unicode
from lib.utils import string_to_list, string_to_dict, get_query_string
from django.utils.safestring import mark_safe
register = template.Library()
@register.inclusion_tag('_response.html', takes_context=True)
def query_string(context, add=None, remove=None):
"""
Allows the addition and removal of query string parameters.
_response.html is just {{ response }}
Usage:
http://www.url.com/{% query_string "param_to_add=value, param_to_add=value" "param_to_remove, params_to_remove" %}
http://www.url.com/{% query_string "" "filter" %}filter={{new_filter}}
http://www.url.com/{% query_string "sort=value" "sort" %}
"""
# Written as an inclusion tag to simplify getting the context.
add = string_to_dict(add)
remove = string_to_list(remove)
params = dict( context['request'].GET.items())
response = get_query_string(params, add, remove)
return {'response': response }
# lib/utils.py
def get_query_string(p, new_params=None, remove=None):
"""
Add and remove query parameters. From `django.contrib.admin`.
"""
if new_params is None: new_params = {}
if remove is None: remove = []
for r in remove:
for k in p.keys():
if k.startswith(r):
del p[k]
for k, v in new_params.items():
if k in p and v is None:
del p[k]
elif v is not None:
p[k] = v
return mark_safe('?' + '&'.join([u'%s=%s' % (k, v) for k, v in p.items()]).replace(' ', '%20'))
def string_to_dict(string):
"""
Usage::
{{ url|thumbnail:"width=10,height=20" }}
{{ url|thumbnail:"width=10" }}
{{ url|thumbnail:"height=20" }}
"""
kwargs = {}
if string:
string = str(string)
if ',' not in string:
# ensure at least one ','
string += ','
for arg in string.split(','):
arg = arg.strip()
if arg == '': continue
kw, val = arg.split('=', 1)
kwargs[kw] = val
return kwargs
def string_to_list(string):
"""
Usage::
{{ url|thumbnail:"width,height" }}
"""
args = []
if string:
string = str(string)
if ',' not in string:
# ensure at least one ','
string += ','
for arg in string.split(','):
arg = arg.strip()
if arg == '': continue
args.append(arg)
return args
Comments
Great, thanks - this totally met my need.
A few notes for others implementing this:
You need to add
"django.core.context_processors.request", to yourTEMPLATE_CONTEXT_PROCESSORSsetting.You need to add this import to your lib/utils.py (or equivalent):
And it is also worth mentioning that you need to create _response.html
#
This is terrific. I don't use GET parameters all that often, but for filtering/sorting content, this snippet really proved helpful. Thanks so much.
#
A great piece of code but it doesn't work with MultipleChoiceField !
The QueryDict object cannot deal with a list values, you have the use the lists function.
A possible implementation might be (for the get_query_string method):
...
def get_query_string(p, new_params=None, remove=None):
#
I've created a snippet that does the same job as this one except that it can resolve context variables and it doesn't require the _response.html template. Check it out here
#
Have just posted this on JHsaunders' snippet, but it seems to apply here too: as it stands, this is vulnerable to a cross-site scripting attack because the URL variables previously provided by the user are passed through mark_safe with no escaping, apart from replacing space characters. This can be fixed by adding 'import urllib' to lib/utils.py, and changing the last line of get_query_string to:
(Also, to be completely correct even when autoescaping is turned off, I suspect it should be using a plain '&' to delimit the arguments and passing it back as an unsafe string for the template layer to escape - but I'll leave that for someone else to confirm...)
#
Here's a list of other implementations I could find for a Django template tag which modifies the current HTTP GET query string.
On djangosnippets.org:
On Stack Overflow:
On PyPI:
On GitHub:
#