from django.utils.encoding import smart_str from django.core.urlresolvers import get_callable, RegexURLResolver, get_resolver from django.template import Node, NodeList, TextNode, TemplateSyntaxError, Library, resolve_variable from django.conf import settings register = Library() def do_ifactive(parser, token): """ Defines a conditional block tag, "ifactive" that switches based on whether the active request is being handled by a particular view (with optional args and kwargs). Has the form: {% ifactive request path.to.view %} [Block to render if path.to.view is the active view] {% else %} [Block to render if path.to.view is not the active view] {% endifactive %} 'request' is a context variable expression which resolves to the HttpRequest object for the current request. (Additionally, the ActiveViewMiddleware must be installed for this to work.) 'path.to.view' can be a string with a python import path (which must be mentioned in the urlconf), or a name of a urlpattern (i.e., same as the argument to the {% url %} tag). You can also pass arguments or keyword arguments in the same form as accepted by the {% url %} tag, e.g.: {% ifactive request path.to.view var1="bar",var2=var.prop %}...{% endifactive %} or: {% ifactive request path.to.view "bar",var.prop %}...{% endifactive %} The else block is optional. """ end_tag = 'endifactive' active_nodes = parser.parse((end_tag,'else')) end_token = parser.next_token() if end_token.contents == 'else': inactive_nodes = parser.parse((end_tag,)) parser.delete_first_token() else: inactive_nodes = None tag_args = token.contents.split(' ') if len(tag_args) < 3: raise TemplateSyntaxError("'%s' takes at least two arguments" " (context variable with the request, and path to a view)" % tag_args[0]) request_var = tag_args[1] view_name = tag_args[2] args, kwargs = _parse_url_args(parser, tag_args[3:]) return ActiveNode(request_var, view_name, args, kwargs, active_nodes, inactive_nodes) register.tag('ifactive', do_ifactive) class ActiveNode(Node): def __init__(self, request_var, view_name, args, kwargs, active_nodes, inactive_nodes=None): self.request_var = request_var self.view_name = view_name self.args = args self.kwargs = kwargs self.active_nodes = active_nodes self.inactive_nodes = inactive_nodes def render(self, context): request = resolve_variable(self.request_var, context) view, default_args = _get_view_and_default_args(self.view_name) if getattr(request, '_view_func', None) is view: resolved_args = [arg.resolve(context) for arg in self.args] if request._view_args == resolved_args: resolved_kwargs = dict([(k, v.resolve(context)) for k, v in self.kwargs.items()]) resolved_kwargs.update(default_args) if request._view_kwargs == resolved_kwargs: return self.active_nodes.render(context) if self.inactive_nodes is not None: return self.inactive_nodes.render(context) else: return '' def _get_patterns_map(resolver, default_args=None): """ Recursively generates a map of (pattern name or path to view function) -> (view function, default args) """ patterns_map = {} if default_args is None: default_args = {} for pattern in resolver.url_patterns: pattern_args = default_args.copy() if isinstance(pattern, RegexURLResolver): pattern_args.update(pattern.default_kwargs) patterns_map.update(_get_patterns_map(pattern, pattern_args)) else: pattern_args.update(pattern.default_args) if pattern.name is not None: patterns_map[pattern.name] = (pattern.callback, pattern_args) # HACK: Accessing private attribute of RegexURLPattern callback_str = getattr(pattern, '_callback_str', None) if callback_str is not None: patterns_map[pattern._callback_str] = (pattern.callback, pattern_args) return patterns_map _view_name_cache = None def _get_view_and_default_args(view_name): """ Given view_name (a path to a view or a name of a urlpattern, returns the view function and a dict containing any default kwargs that are specified in the urlconf for that view. """ global _view_name_cache if _view_name_cache is None: _view_name_cache = _get_patterns_map(get_resolver(None)) try: return _view_name_cache[view_name] except KeyError: raise KeyError("%s does not match any urlpatterns" % view_name) def _parse_url_args(parser, bits): """ Parses URL parameters in the same way as the {% url %} tag. """ args = [] kwargs = {} for bit in bits: for arg in bit.split(","): if '=' in arg: k, v = arg.split('=', 1) k = k.strip() kwargs[smart_str(k,'ascii')] = parser.compile_filter(v) elif arg: args.append(parser.compile_filter(arg)) return args, kwargs class ActiveViewMiddleware(object): def process_view(self, request, view_func, view_args, view_kwargs): """ Records the view function used on this request and its *args and **kwargs. Needed by {% ifactive %} to determine if a particular view is currently active. """ request._view_func = view_func request._view_args = list(view_args) request._view_kwargs = view_kwargs