Skip to content

Commit

Permalink
Merge branch 'url-tag-asvar'
Browse files Browse the repository at this point in the history
git-svn-id: http://code.djangoproject.com/svn/django/trunk@8716 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
jacobian committed Aug 29, 2008
1 parent 2ca8cf3 commit c068bc1
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 12 deletions.
51 changes: 39 additions & 12 deletions django/template/defaulttags.py
Expand Up @@ -351,22 +351,40 @@ def render(self, context):
return self.mapping.get(self.tagtype, '') return self.mapping.get(self.tagtype, '')


class URLNode(Node): class URLNode(Node):
def __init__(self, view_name, args, kwargs): def __init__(self, view_name, args, kwargs, asvar):
self.view_name = view_name self.view_name = view_name
self.args = args self.args = args
self.kwargs = kwargs self.kwargs = kwargs
self.asvar = asvar


def render(self, context): def render(self, context):
from django.core.urlresolvers import reverse, NoReverseMatch from django.core.urlresolvers import reverse, NoReverseMatch
args = [arg.resolve(context) for arg in self.args] args = [arg.resolve(context) for arg in self.args]
kwargs = dict([(smart_str(k,'ascii'), v.resolve(context)) kwargs = dict([(smart_str(k,'ascii'), v.resolve(context))
for k, v in self.kwargs.items()]) for k, v in self.kwargs.items()])


# Try to look up the URL twice: once given the view name, and again
# relative to what we guess is the "main" app. If they both fail,
# re-raise the NoReverseMatch unless we're using the
# {% url ... as var %} construct in which cause return nothing.
url = ''
try: try:
return reverse(self.view_name, args=args, kwargs=kwargs) url = reverse(self.view_name, args=args, kwargs=kwargs)
except NoReverseMatch: except NoReverseMatch:
project_name = settings.SETTINGS_MODULE.split('.')[0] project_name = settings.SETTINGS_MODULE.split('.')[0]
return reverse(project_name + '.' + self.view_name, try:
args=args, kwargs=kwargs) url = reverse(project_name + '.' + self.view_name,
args=args, kwargs=kwargs)
except NoReverseMatch:
if self.asvar is None:
raise

if self.asvar:
context[self.asvar] = url
return ''
else:
return url


class WidthRatioNode(Node): class WidthRatioNode(Node):
def __init__(self, val_expr, max_expr, max_width): def __init__(self, val_expr, max_expr, max_width):
Expand Down Expand Up @@ -1041,21 +1059,30 @@ def url(parser, token):
The URL will look like ``/clients/client/123/``. The URL will look like ``/clients/client/123/``.
""" """
bits = token.contents.split(' ', 2) bits = token.contents.split(' ')
if len(bits) < 2: if len(bits) < 2:
raise TemplateSyntaxError("'%s' takes at least one argument" raise TemplateSyntaxError("'%s' takes at least one argument"
" (path to a view)" % bits[0]) " (path to a view)" % bits[0])
viewname = bits[1]
args = [] args = []
kwargs = {} kwargs = {}
asvar = None

if len(bits) > 2: if len(bits) > 2:
for arg in bits[2].split(','): bits = iter(bits[2:])
if '=' in arg: for bit in bits:
k, v = arg.split('=', 1) if bit == 'as':
k = k.strip() asvar = bits.next()
kwargs[k] = parser.compile_filter(v) break
else: else:
args.append(parser.compile_filter(arg)) for arg in bit.split(","):
return URLNode(bits[1], args, kwargs) 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 URLNode(viewname, args, kwargs, asvar)
url = register.tag(url) url = register.tag(url)


#@register.tag #@register.tag
Expand Down
23 changes: 23 additions & 0 deletions docs/ref/templates/builtins.txt
Expand Up @@ -675,6 +675,29 @@ The template tag will output the string ``/clients/client/123/``.
<naming-url-patterns>`, you can refer to the name of the pattern in the ``url`` <naming-url-patterns>`, you can refer to the name of the pattern in the ``url``
tag instead of using the path to the view. tag instead of using the path to the view.


Note that if the URL you're reversing doesn't exist, you'll get an
:exc:`NoReverseMatch` exception raised, which will cause your site to display an
error page.

**New in development verson:** If you'd like to retrieve a URL without displaying it,
you can use a slightly different call:

.. code-block:: html+django

{% url path.to.view arg, arg2 as the_url %}

<a href="{{ the_url }}">I'm linking to {{ the_url }}</a>

This ``{% url ... as var %}`` syntax will *not* cause an error if the view is
missing. In practice you'll use this to link to views that are optional:

.. code-block:: html+django

{% url path.to.view as the_url %}
{% if the_url %}
<a href="{{ the_url }}">Link to optional stuff</a>
{% endif %}

.. templatetag:: widthratio .. templatetag:: widthratio


widthratio widthratio
Expand Down
5 changes: 5 additions & 0 deletions tests/regressiontests/templates/tests.py
Expand Up @@ -896,6 +896,11 @@ def get_template_tests(self):
'url-fail02': ('{% url no_such_view %}', {}, urlresolvers.NoReverseMatch), 'url-fail02': ('{% url no_such_view %}', {}, urlresolvers.NoReverseMatch),
'url-fail03': ('{% url regressiontests.templates.views.client %}', {}, urlresolvers.NoReverseMatch), 'url-fail03': ('{% url regressiontests.templates.views.client %}', {}, urlresolvers.NoReverseMatch),


# {% url ... as var %}
'url-asvar01': ('{% url regressiontests.templates.views.index as url %}', {}, ''),
'url-asvar02': ('{% url regressiontests.templates.views.index as url %}{{ url }}', {}, '/url_tag/'),
'url-asvar03': ('{% url no_such_view as url %}{{ url }}', {}, ''),

### CACHE TAG ###################################################### ### CACHE TAG ######################################################
'cache01': ('{% load cache %}{% cache -1 test %}cache01{% endcache %}', {}, 'cache01'), 'cache01': ('{% load cache %}{% cache -1 test %}cache01{% endcache %}', {}, 'cache01'),
'cache02': ('{% load cache %}{% cache -1 test %}cache02{% endcache %}', {}, 'cache02'), 'cache02': ('{% load cache %}{% cache -1 test %}cache02{% endcache %}', {}, 'cache02'),
Expand Down

0 comments on commit c068bc1

Please sign in to comment.