1
2
3
4
5 """
6 source: http://djangosnippets.org/snippets/1350/
7
8 A smarter {% if %} tag for django templates.
9
10 While retaining current Django functionality, it also handles equality,
11 greater than and less than operators. Some common case examples::
12
13 {% if articles|length >= 5 %}...{% endif %}
14 {% if "ifnotequal tag" != "beautiful" %}...{% endif %}
15 """
16
17 import traceback
18 import logging
19
20 from django import template
21
22 register = template.Library()
23
24 logger = logging.getLogger('conditional_tags')
27
28 - def __init__(self, var1, var2=None, negate=False):
29 self.var1 = var1
30 self.var2 = var2
31 self.negate = negate
32
34 try:
35 var1, var2 = self.resolve_vars(context)
36 outcome = self.calculate(var1, var2)
37 except:
38 outcome = False
39 if self.negate:
40 return not outcome
41 return outcome
42
44 var2 = self.var2 and self.var2.resolve(context)
45 return self.var1.resolve(context), var2
46
48 raise NotImplementedError()
49
50
51 -class Or(BaseCalc):
54
55
56 -class And(BaseCalc):
59
64
69
74
75
76 -class In(BaseCalc):
79
80
81
82 OPERATORS = {
83 '=': (Equals, True),
84 '==': (Equals, True),
85 '!=': (Equals, False),
86 '>': (Greater, True),
87 '>=': (GreaterOrEqual, True),
88 '<=': (Greater, False),
89 '<': (GreaterOrEqual, False),
90 'or': (Or, True),
91 'and': (And, True),
92 'in': (In, True),
93 }
94 BOOL_OPERATORS = ('or', 'and')
98
102
105
107 self._tokens = tokens
108 self.len = len(tokens)
109 self.pos = 0
110
111 tokens = property(_get_tokens, _set_tokens)
112
122
123 - def get_token(self, eof_message=None, lookahead=False):
124 negate = True
125 token = None
126 pos = self.pos
127 while token is None or token == 'not':
128 if pos >= self.len:
129 if eof_message is None:
130 raise self.error_class()
131 raise self.error_class(eof_message)
132 token = self.tokens[pos]
133 negate = not negate
134 pos += 1
135 if not lookahead:
136 self.pos = pos
137 return token, negate
138
140 return self.pos >= self.len
141
144
146 var = self.get_var()
147 if not self.at_end():
148 op_token = self.get_token(lookahead=True)[0]
149 if isinstance(op_token, basestring) and (op_token not in
150 BOOL_OPERATORS):
151 op, negate = self.get_operator()
152 return op(var, self.get_var(), negate=negate)
153 return var
154
156 token, negate = self.get_token('Reached end of statement, still '
157 'expecting a variable.')
158 if isinstance(token, basestring) and token in OPERATORS:
159 raise self.error_class('Expected variable, got operator (%s).' %
160 token)
161 var = self.create_var(token)
162 if negate:
163 return Or(var, negate=True)
164 return var
165
167 token, negate = self.get_token('Reached end of statement, still '
168 'expecting an operator.')
169 if not isinstance(token, basestring) or token not in OPERATORS:
170 raise self.error_class('%s is not a valid operator.' % token)
171 if self.at_end():
172 raise self.error_class('No variable provided after "%s".' % token)
173 op, true = OPERATORS[token]
174 if not true:
175 negate = not negate
176 return op, negate
177
179 error_class = template.TemplateSyntaxError
180
181 - def __init__(self, parser, *args, **kwargs):
184
186 return self.template_parser.compile_filter(value)
187
190 - def __init__(self, var, nodelist_true, nodelist_false=None):
191 self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
192 self.var = var
193
195 if self.var.resolve(context):
196 return self.nodelist_true.render(context)
197 if self.nodelist_false:
198 return self.nodelist_false.render(context)
199 return ''
200
202 return "<Smart If node>"
203
205 for node in self.nodelist_true:
206 yield node
207 if self.nodelist_false:
208 for node in self.nodelist_false:
209 yield node
210
219
220 @register.tag('if')
222 bits = token.split_contents()[1:]
223 var = TemplateIfParser(parser, bits).parse()
224 nodelist_true = parser.parse(('else', 'endif'))
225 token = parser.next_token()
226 if token.contents == 'else':
227 nodelist_false = parser.parse(('endif',))
228 parser.delete_first_token()
229 else:
230 nodelist_false = None
231 return SmartIfNode(var, nodelist_true, nodelist_false)
232
233
234
235
236
237
238 -class TestVar(object):
239 """
240 A basic self-resolvable object similar to a Django template variable. Used
241 to assist with tests.
242 """
245
248