Created
January 6, 2010 18:20
-
-
Save bfirsh/270491 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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