Skip to content

Instantly share code, notes, and snippets.

@bfirsh
Created January 6, 2010 18:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bfirsh/270491 to your computer and use it in GitHub Desktop.
Save bfirsh/270491 to your computer and use it in GitHub Desktop.
"""
A drop in replacement for {% url %} that generates relative paths.
Just put {% load relative_urls %} at the top of your template, and all {% url %}
calls will generate paths relative to the current request path.
For example, when reversing a URL that points to ``/admin/foo/`` on the page
``/admin/bar/``, it will output ``../foo/``.
"""
from django import template
from django.core.exceptions import ImproperlyConfigured
register = template.Library()
class RelativeURLNode(template.defaulttags.URLNode):
def render(self, context):
if 'request' not in context:
raise ImproperlyConfigured('You need to use RequestContext to use relative URLs.')
target_url = super(RelativeURLNode, self).render(context)
if self.asvar:
target_url = context[self.asvar]
target = target_url.split('/')
base = context['request'].path.split('/')
common = 0
for i in xrange(min(len(base), len(target))):
if base[i] == target[i]:
common += 1
else:
break
# ".." for each dir not in common, plus the leftover on the target
output = '/'.join(['..'] * (len(base) - common - 1) + target[common:])
if self.asvar:
context[self.asvar] = output
else:
return output
return ''
def url(parser, token):
"""
Returns a URL relative to the current request's path matching given view
with its parameters.
This is a way to define links that aren't tied to a particular URL
configuration::
{% url path.to.some_view arg1,arg2,name1=value1 %}
The first argument is a path to a view. It can be an absolute python path
or just ``app_name.view_name`` without the project name if the view is
located inside the project. Other arguments are comma-separated values
that will be filled in place of positional and keyword arguments in the
URL. All arguments for the URL should be present.
For example if you have a view ``app_name.client`` taking client's id and
the corresponding line in a URLconf looks like this::
('^client/(\d+)/$', 'app_name.client')
and this app's URLconf is included into the project's URLconf under some
path::
('^clients/', include('project_name.app_name.urls'))
then in a template you can create a link for a certain client like this::
{% url app_name.client client.id %}
Suppose the current path is ``/another-path/``. The URL will look like
``../client/123/``.
"""
bits = token.split_contents()
if len(bits) < 2:
raise template.TemplateSyntaxError("'%s' takes at least one argument"
" (path to a view)" % bits[0])
viewname = bits[1]
args = []
kwargs = {}
asvar = None
if len(bits) > 2:
bits = iter(bits[2:])
for bit in bits:
if bit == 'as':
asvar = bits.next()
break
else:
for arg in bit.split(","):
if '=' in arg:
k, v = arg.split('=', 1)
k = k.strip()
kwargs[k] = parser.compile_filter(v)
elif arg:
args.append(parser.compile_filter(arg))
return RelativeURLNode(viewname, args, kwargs, asvar)
url = register.tag(url)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment