Login

Modify query string on a url

Author:
dnordberg
Posted:
June 27, 2008
Language:
Python
Version:
.96
Score:
4 (after 4 ratings)

Modify a query string on a url. The comments in the code should explain sufficiently. String_to_dict, and string_to_list are also useful for templatetags that require variable arguments.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# 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

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
  5. Help text hyperlinks by sa2812 1 year, 7 months ago

Comments

davenaff (on September 17, 2008):

Great, thanks - this totally met my need.

A few notes for others implementing this:

You need to add "django.core.context_processors.request", to your TEMPLATE_CONTEXT_PROCESSORS setting.

You need to add this import to your lib/utils.py (or equivalent):

from django.utils.safestring import mark_safe

And it is also worth mentioning that you need to create _response.html

#

worksology (on November 30, 2009):

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.

#

mlhamel (on August 27, 2010):

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):

"""
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
url = ''
for k, v in p.lists():
    for element in v:
        if len(url) == 0:
            url += '?'
        else:
            url += '&amp';
        url+= ''.join('%s=%s' % (k, element)).replace(' ', '%20')
return mark_safe(url)

#

mattwestcott (on November 27, 2010):

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:

return mark_safe('?' + '&'.join([u'%s=%s' % (urllib.quote_plus(str(k)), urllib.quote_plus(str(v))) for k, v in p.items()]))

(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...)

#

Please login first before commenting.