2 The node template tags are automatically included as builtins if :mod:`philo` is an installed app.
4 .. templatetag:: node_url
9 The :ttag:`node_url` tag allows access to :meth:`.View.reverse` from a template for a :class:`.Node`. By default, the :class:`.Node` that is used for the call is pulled from the context variable ``node``; however, this can be overridden with the ``[for <node>]`` option.
13 {% node_url [for <node>] [as <var>] %}
14 {% node_url with <obj> [for <node>] [as <var>] %}
15 {% node_url <view_name> [<arg1> [<arg2> ...] ] [for <node>] [as <var>] %}
16 {% node_url <view_name> [<key1>=<value1> [<key2>=<value2> ...] ] [for <node>] [as <var>] %}
20 from django import template
21 from django.conf import settings
22 from django.contrib.sites.models import Site
23 from django.core.urlresolvers import reverse, NoReverseMatch
24 from django.template.defaulttags import kwarg_re
25 from django.utils.encoding import smart_str
27 from philo.exceptions import ViewCanNotProvideSubpath
30 register = template.Library()
33 class NodeURLNode(template.Node):
34 def __init__(self, node, as_var, with_obj=None, view_name=None, args=None, kwargs=None):
36 self.view_name = view_name
38 # Because the following variables have already been compiled as filters if they exist, they don't need to be re-scanned as template variables.
40 self.with_obj = with_obj
44 def render(self, context):
46 node = self.node.resolve(context)
48 node = context.get('node', None)
51 return settings.TEMPLATE_STRING_IF_INVALID
53 if self.with_obj is None and self.view_name is None:
54 url = node.get_absolute_url()
56 if not node.view.accepts_subpath:
57 return settings.TEMPLATE_STRING_IF_INVALID
59 if self.with_obj is not None:
61 view_name, args, kwargs = node.view.get_reverse_params(self.with_obj.resolve(context))
62 except ViewCanNotProvideSubpath:
63 return settings.TEMPLATE_STRING_IF_INVALID
64 else: # self.view_name is not None
65 view_name = self.view_name
66 args = [arg.resolve(context) for arg in self.args]
67 kwargs = dict([(smart_str(k, 'ascii'), v.resolve(context)) for k, v in self.kwargs.items()])
71 subpath = reverse(view_name, urlconf=node.view, args=args, kwargs=kwargs)
72 except NoReverseMatch:
73 if self.as_var is None:
74 if settings.TEMPLATE_DEBUG:
76 return settings.TEMPLATE_STRING_IF_INVALID
78 url = node.construct_url(subpath)
81 context[self.as_var] = url
87 @register.tag(name='node_url')
88 def do_node_url(parser, token):
90 {% node_url [for <node>] [as <var>] %}
91 {% node_url with <obj> [for <node>] [as <var>] %}
92 {% node_url <view_name> [<arg1> [<arg2> ...] ] [for <node>] [as <var>] %}
93 {% node_url <view_name> [<key1>=<value1> [<key2>=<value2> ...] ] [for <node>] [as <var>] %}
96 params = token.split_contents()
103 if len(params) >= 2 and params[-2] == 'as':
107 if len(params) >= 2 and params[-2] == 'for':
108 node = parser.compile_filter(params[-1])
111 if len(params) >= 2 and params[-2] == 'with':
112 with_obj = parser.compile_filter(params[-1])
115 if with_obj is not None:
117 raise template.TemplateSyntaxError('`%s` template tag accepts no arguments or keyword arguments if with <obj> is specified.' % tag)
118 return NodeURLNode(with_obj=with_obj, node=node, as_var=as_var)
123 view_name = params.pop(0)
125 match = kwarg_re.match(param)
127 raise TemplateSyntaxError("Malformed arguments to `%s` tag" % tag)
128 name, value = match.groups()
130 kwargs[name] = parser.compile_filter(value)
132 args.append(parser.compile_filter(value))
133 return NodeURLNode(view_name=view_name, args=args, kwargs=kwargs, node=node, as_var=as_var)
135 return NodeURLNode(node=node, as_var=as_var)