Url persistance of GET variables

  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. [middleware] Rewrite anchors to point into Coral CDN by crime_minister 4 years, 9 months ago
  2. {% with %} template tag by SmileyChris 6 years, 2 months ago
  3. Load initial form fields from GET parameters by ramen 3 years, 6 months ago
  4. Export Models by brunobord 4 years, 11 months ago
  5. Using dojo/json to check for username availability by heckj 5 years, 11 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.

#

(Forgotten your password?)