1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172 | 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
|
Comments