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 | from django import template
from django.template.base import TextNode, VariableNode, TemplateSyntaxError
from copy import copy
register = template.Library()
@register.tag( name = "menu" )
def do_menu( parser, token ):
tag_name, menu_name, opt_name = token.split_contents()
return MenuNode( menu_name, opt_name )
class MenuNode( template.Node ):
def __init__( self, menu_name, opt_name ):
self.menu_name = menu_name
self.opt_name = opt_name
def _replaceUrlText( self, opt, active ):
active_ = copy( active )
for i in range( 0, len( active ) ):
node = active_[ i ]
if isinstance( node, VariableNode ):
if node.filter_expression.token == u'text':
active_[ i ] = TextNode( opt[ 1 ][ 0 ].s )
elif node.filter_expression.token == u'url':
active_[ i ] = TextNode( opt[ 0 ] )
return active_
def render( self, context ):
menu = context[ '_menus' ][ self.menu_name ]
active = menu[ '_defmenu_active' ]
inactive = menu[ '_defmenu_inactive' ]
opts = menu[ '_defmenu_opts' ]
opt = opts[ self.opt_name ]
result_list = []
for opt_name in opts:
opt = opts[ opt_name ]
if self.opt_name == opt_name:
result_list.append( ( self._replaceUrlText( opt, active ) ).render( context ) )
else:
result_list.append( ( self._replaceUrlText( opt, inactive ) ).render( context ) )
result_list.reverse()
return " ".join( result_list )
@register.tag( name = 'defmenu' )
def do_defmenu( parser, token ):
nodelist = parser.parse( ( 'enddefmenu', ) )
parser.delete_first_token()
tag_name, menu_name = token.split_contents()
return DefMenuNode( nodelist, menu_name )
class DefMenuNode( template.Node ):
def __init__( self, nodelist, menu_name ):
self.nodelist = nodelist
self.menu_name = menu_name
def render( self, context ):
output = self.nodelist.render( context ) # applye children here
if '_menus' not in context:
context[ '_menus' ] = {}
menu_name = self.menu_name
context[ '_menus' ][ menu_name ] = {}
menu = context[ '_menus' ][ menu_name ]
try:
menu[ '_defmenu_active' ] = context[ '_defmenu_active' ]
except KeyError:
raise TemplateSyntaxError( "An {% active %} block is not found inside the {% defmenu %} block." )
try:
menu[ '_defmenu_inactive' ] = context[ '_defmenu_inactive' ]
except KeyError:
raise TemplateSyntaxError( "An {% inactive %} block is not found inside the {% defmenu %} block." )
try:
menu[ '_defmenu_opts' ] = context[ '_defmenu_opts' ]
except KeyError:
raise TemplateSyntaxError( "The {% defmenu %} block must have at least one {% opt %} block inside." )
del context[ '_defmenu_active' ]
del context[ '_defmenu_inactive' ]
del context[ '_defmenu_opts' ]
return ""
@register.tag( name = 'active' )
def do_active( parser, token ):
nodelist = parser.parse( ( 'endactive', ) )
parser.delete_first_token()
return ActiveNode( nodelist )
class ActiveNode( template.Node ):
def __init__( self, nodelist ):
self.nodelist = nodelist
def render( self, context ):
context[ '_defmenu_active' ] = self.nodelist
@register.tag( name = 'inactive' )
def do_inactive( parser, token ):
nodelist = parser.parse( ( 'endinactive', ) )
parser.delete_first_token()
return InactiveNode( nodelist )
class InactiveNode( template.Node ):
def __init__( self, nodelist ):
self.nodelist = nodelist
def render( self, context ):
context[ '_defmenu_inactive' ] = self.nodelist
@register.tag( name = 'opt' )
def do_opt( parser, token ):
nodelist = parser.parse( ( 'endopt', ) )
parser.delete_first_token()
tag_name, name, url = token.split_contents()
return OptNode( nodelist, name, url )
class OptNode( template.Node ):
def __init__( self, nodelist, name, url ):
self.nodelist = nodelist
self.name = name
self.url = url
def render( self, context ):
if '_defmenu_opts' not in context:
context['_defmenu_opts' ] = {}
context[ '_defmenu_opts' ].update( { self.name: ( self.url, self.nodelist ) } )
|
Comments
For a much simpler approach, check out my solution: http://djangosnippets.org/snippets/2421/
#
Thank you! When I created a snippet #2722 I did not take into account the option with of inclusion tag.
I think your solution is better.
My decision have only one plus (your have several ones). In case of #2722 inheritance is possible. Sometimes it is more convenient and conceptual than inclusion.
And one more plus in #2722. In your case if you want to mek whole fragment different (not only class attribute) you should write several times very similar code for each option. But I would like DRY menu.
#