Login

Url persistance of GET variables

Author:
alex_ndc
Posted:
November 9, 2008
Language:
Python
Version:
1.0
Score:
1 (after 1 ratings)

I'm using Django 0.96 for a project, and the url tag does not have all the capabilities I need.

I want a way to persist all or some of the GET parameters between requests. I also want a way to add to the current url a list of extra parameters, or nullify certain parameters.

In this snippet I am defining 2 tags ... link_persist and link_add. It only works with Django 0.96, as the API changed ... but maybe a good soul can fix it for 1.0, as I haven't had any time available.

A tip for usage: if you specify a parameter as being the empty string, or None, the parameter will be removed from the link. When you specify a parameter already available in GET, it will replace it.

  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
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
from django.core.urlresolvers import reverse
from django.template import FilterExpression
from django.template import Library, Node, TemplateSyntaxError, resolve_variable
from re import split
from urllib import quote,unquote
import re

register = Library()

class LinkNode(Node):
	def __init__(self, viewname, args, kwargs, add_to_current_link=False):
		self.viewname = viewname
		self.kwargs = kwargs
		self.args = args
		self.add_to_current_link = add_to_current_link
		
	def render(self,context):
		req = resolve_variable('request',context)

		args = [isinstance(k,FilterExpression) and k.resolve(context) or k for k in self.args]
		kwargs = req.GET.copy()

		for k,v in self.kwargs.items():
			if isinstance(v, FilterExpression) and v.token:
				kwargs[k] = v.resolve(context)
			elif v:
				kwargs[k] = v 
		
		try:
			url = not self.add_to_current_link and reverse(viewname=self.viewname, args=args, kwargs=None) or req.path
		except:
			url = req.path
	
		# check to see if named arguments have been passed
		query_started = url.find('?') > -1
		query_string  = ""

		raw_url = unquote(url)

		for k,values in kwargs.items():
			if not isinstance(values,list):
				values = [values]
			
			params_added = "&".join(
				[quote(k) + "=" + quote(isinstance(v,str) and v or str(v)) for v in values 
					if v and (not query_started or not re.search(r"\b" + re.escape(k + "=" + v) + r"\b", raw_url))])

			if params_added: query_string += "&" + params_added
		

		if not query_started and len(query_string) > 0:
			query_string = "?" + query_string[1:]

		return url + query_string
			

def link_persist(parser, token):
	"""Url Tag that persists GET variables

	It has the same behaviour as the standard url tag
	found in django.template.defaulttags.

	But GET variables are passed by default to the 'reverse' url 
	method that builds the final url. Specified parameters override
	GET parameters.

	Usage: {% link_persist viewname arg1, arg2, ... karg1=val1, karg2=val2 ... %}

	This has been tested with Django version 0.96"""

	bits = token.contents.split(' ', 2)
	if len(bits) < 2:
		raise TemplateSyntaxError, "'%s' takes at least one argument (path to a view)" % bits[0]

	args = []
	kwargs = {}

	if len(bits) > 2:
		for arg in bits[2].split(','):
			if '=' in arg:
				k, v = arg.split('=', 1)
				kwargs[k] = parser.compile_filter(v)
			else:
				args.append(parser.compile_filter(arg))

	return LinkNode(bits[1], args, kwargs)


def link_add(parser, token):
	"""Tag that adds parameters to the current url.

	Usage: {% link_add karg1=val1, karg2=val2 ... %}

	This has been tested with Django version 0.96"""

	bits = token.contents.split(' ', 2)

	args = []
	kwargs = {}
	if len(bits) > 1:
		for arg in bits[1].split(','):
			if '=' in arg:
				k, v = arg.split('=', 1)
				kwargs[k] = parser.compile_filter(v)

	return LinkNode(None, args, kwargs, True)


register.tag('link_persist', link_persist)
register.tag('link_add', link_add)

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, 8 months ago

Comments

brian (on November 9, 2008):

That looks a bit complicated for something that should be so simple. I use this, which works for all my purposes:

@register.simple_tag  
def args(vars, var, value):
    vars = vars.copy()
    vars[var] = value
    return vars.urlencode()

It works great for dealing with paged content:

href="?{% args request.GET 'page' page.previous_page_number %}"

#

alex_ndc (on November 10, 2008):

That's cool, although my needs a little more complicated.

I'll work on simplifying it ... my python-foo is a little rusty :)

#

sturm (on July 17, 2012):

I used this snippet for quite some time and finally found a bug :-)

You have the following line in the LinkNode:

for k,values in kwargs.items():

but it must be:

for k,values in kwargs.lists():

otherwise you will only get the last value for each key.

#

Please login first before commenting.