Merge branch 'url-tag-asvar'

git-svn-id: http://code.djangoproject.com/svn/django/trunk@8716 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jacob Kaplan-Moss 2008-08-29 19:28:03 +00:00
parent 2ca8cf3628
commit c068bc184c
3 changed files with 67 additions and 12 deletions

View File

@ -351,22 +351,40 @@ class TemplateTagNode(Node):
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:
url = reverse(project_name + '.' + self.view_name,
args=args, kwargs=kwargs) 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):
@ -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:])
for bit in bits:
if bit == 'as':
asvar = bits.next()
break
else:
for arg in bit.split(","):
if '=' in arg: if '=' in arg:
k, v = arg.split('=', 1) k, v = arg.split('=', 1)
k = k.strip() k = k.strip()
kwargs[k] = parser.compile_filter(v) kwargs[k] = parser.compile_filter(v)
else: elif arg:
args.append(parser.compile_filter(arg)) args.append(parser.compile_filter(arg))
return URLNode(bits[1], args, kwargs) return URLNode(viewname, args, kwargs, asvar)
url = register.tag(url) url = register.tag(url)
#@register.tag #@register.tag

View File

@ -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

View File

@ -896,6 +896,11 @@ class Templates(unittest.TestCase):
'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'),