Skip to content

Instantly share code, notes, and snippets.

@etchalon
Last active October 28, 2018 07:03
Show Gist options
  • Save etchalon/4493752 to your computer and use it in GitHub Desktop.
Save etchalon/4493752 to your computer and use it in GitHub Desktop.
Query String alteration template tag for Django.
"""
Query String manipulation filters
"""
import logging
from django import template
from django.http import QueryDict
from django.utils.translation import ugettext as _
register = template.Library()
class QueryStringAlterer(template.Node):
"""
Query String alteration template tag
Receives a query string (either text or a QueryDict such as request.GET)
and a list of changes to apply. The result will be returned as text query
string, allowing use like this::
<a href="?{% qs_alter request.GET type=object.type %}">{{ label }}</a>
There are four available alterations:
Assignment:
name=var
Deletion - removes the named parameter:
-name
Appending (treating the qstring as a list):
name+=var
Removing (treating the qstring as a list):
name-=var
Examples:
Query string provided as QueryDict::
{% qs_alter request.GET foo=bar %}
{% qs_alter request.GET foo=bar baaz=quux %}
{% qs_alter request.GET foo+=bar %}
{% qs_alter request.GET foo-=bar %}
{% qs_alter request.GET foo=bar baaz+=quux -corge %}
Query string provided as string::
{% qs_alter "foo=baaz" foo=bar %}">
"""
def __init__(self, base_qs, *args):
self.base_qs = template.Variable(base_qs)
self.args = args
def render(self, context):
base_qs = self.base_qs.resolve(context)
if isinstance(base_qs, QueryDict):
qs = base_qs.copy()
else:
qs = QueryDict(base_qs, mutable=True)
for arg in self.args:
# look for += (add to assumed list or concat to string)
if arg.find('+=') > -1:
k,v = arg.split("+=", 2)
value = template.Variable(v).resolve(context)
if k in qs:
qs[k] = '%s,%s' % (qs[k],str(value))
else:
qs[k] = str(value)
# look for -= (remove from assumed list or string)
elif arg.find('-=') > -1:
k,v = arg.split("-=", 2)
value = template.Variable(v).resolve(context)
if k in qs:
ql = qs[k].split(',')
if str(value) in ql:
ql.remove(str(value))
if len(ql) > 0:
qs[k] = ','.join(ql)
else:
del qs[k]
# look for just = (set, overriding existing value if it exists)
elif arg.find('=') > -1:
k, v = arg.split("=", 2)
if k in qs:
qs[k] = template.Variable(v).resolve(context)
else:
qs[k] = template.Variable(v).resolve(context)
# look for just - (remove from dict)
elif arg.find('-') > -1:
k, v = arg.split("-", 2)
if k in qs:
del qs[k]
return qs.urlencode(safe=',')
@classmethod
def qs_alter_tag(cls, parser, token):
try:
bits = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError(
_('qs_alter requires at least two arguments: the initial query string and at least one alteration')
)
return QueryStringAlterer(bits[1], *bits[2:])
register.tag('qs_alter', QueryStringAlterer.qs_alter_tag)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment