| 1 |
"Default tags used by the template system, available to all templates." |
|---|
| 2 |
|
|---|
| 3 |
from django.template import Node, NodeList, Template, Context, resolve_variable |
|---|
| 4 |
from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END |
|---|
| 5 |
from django.template import get_library, Library, InvalidTemplateLibrary |
|---|
| 6 |
from django.conf import settings |
|---|
| 7 |
import sys |
|---|
| 8 |
|
|---|
| 9 |
register = Library() |
|---|
| 10 |
|
|---|
| 11 |
class CommentNode(Node): |
|---|
| 12 |
def render(self, context): |
|---|
| 13 |
return '' |
|---|
| 14 |
|
|---|
| 15 |
class CycleNode(Node): |
|---|
| 16 |
def __init__(self, cyclevars, variable_name=None): |
|---|
| 17 |
self.cyclevars = cyclevars |
|---|
| 18 |
self.cyclevars_len = len(cyclevars) |
|---|
| 19 |
self.counter = -1 |
|---|
| 20 |
self.variable_name = variable_name |
|---|
| 21 |
|
|---|
| 22 |
def render(self, context): |
|---|
| 23 |
self.counter += 1 |
|---|
| 24 |
value = self.cyclevars[self.counter % self.cyclevars_len] |
|---|
| 25 |
if self.variable_name: |
|---|
| 26 |
context[self.variable_name] = value |
|---|
| 27 |
return value |
|---|
| 28 |
|
|---|
| 29 |
class DebugNode(Node): |
|---|
| 30 |
def render(self, context): |
|---|
| 31 |
from pprint import pformat |
|---|
| 32 |
output = [pformat(val) for val in context] |
|---|
| 33 |
output.append('\n\n') |
|---|
| 34 |
output.append(pformat(sys.modules)) |
|---|
| 35 |
return ''.join(output) |
|---|
| 36 |
|
|---|
| 37 |
class FilterNode(Node): |
|---|
| 38 |
def __init__(self, filter_expr, nodelist): |
|---|
| 39 |
self.filter_expr, self.nodelist = filter_expr, nodelist |
|---|
| 40 |
|
|---|
| 41 |
def render(self, context): |
|---|
| 42 |
output = self.nodelist.render(context) |
|---|
| 43 |
# apply filters |
|---|
| 44 |
return self.filter_expr.resolve(Context({'var': output})) |
|---|
| 45 |
|
|---|
| 46 |
class FirstOfNode(Node): |
|---|
| 47 |
def __init__(self, vars): |
|---|
| 48 |
self.vars = vars |
|---|
| 49 |
|
|---|
| 50 |
def render(self, context): |
|---|
| 51 |
for var in self.vars: |
|---|
| 52 |
try: |
|---|
| 53 |
value = resolve_variable(var, context) |
|---|
| 54 |
except VariableDoesNotExist: |
|---|
| 55 |
continue |
|---|
| 56 |
if value: |
|---|
| 57 |
return str(value) |
|---|
| 58 |
return '' |
|---|
| 59 |
|
|---|
| 60 |
class ForNode(Node): |
|---|
| 61 |
def __init__(self, loopvar, sequence, reversed, nodelist_loop): |
|---|
| 62 |
self.loopvar, self.sequence = loopvar, sequence |
|---|
| 63 |
self.reversed = reversed |
|---|
| 64 |
self.nodelist_loop = nodelist_loop |
|---|
| 65 |
|
|---|
| 66 |
def __repr__(self): |
|---|
| 67 |
if self.reversed: |
|---|
| 68 |
reversed = ' reversed' |
|---|
| 69 |
else: |
|---|
| 70 |
reversed = '' |
|---|
| 71 |
return "<For Node: for %s in %s, tail_len: %d%s>" % \ |
|---|
| 72 |
(self.loopvar, self.sequence, len(self.nodelist_loop), reversed) |
|---|
| 73 |
|
|---|
| 74 |
def __iter__(self): |
|---|
| 75 |
for node in self.nodelist_loop: |
|---|
| 76 |
yield node |
|---|
| 77 |
|
|---|
| 78 |
def get_nodes_by_type(self, nodetype): |
|---|
| 79 |
nodes = [] |
|---|
| 80 |
if isinstance(self, nodetype): |
|---|
| 81 |
nodes.append(self) |
|---|
| 82 |
nodes.extend(self.nodelist_loop.get_nodes_by_type(nodetype)) |
|---|
| 83 |
return nodes |
|---|
| 84 |
|
|---|
| 85 |
def render(self, context): |
|---|
| 86 |
nodelist = NodeList() |
|---|
| 87 |
if context.has_key('forloop'): |
|---|
| 88 |
parentloop = context['forloop'] |
|---|
| 89 |
else: |
|---|
| 90 |
parentloop = {} |
|---|
| 91 |
context.push() |
|---|
| 92 |
try: |
|---|
| 93 |
values = self.sequence.resolve(context, True) |
|---|
| 94 |
except VariableDoesNotExist: |
|---|
| 95 |
values = [] |
|---|
| 96 |
if values is None: |
|---|
| 97 |
values = [] |
|---|
| 98 |
if not hasattr(values, '__len__'): |
|---|
| 99 |
values = list(values) |
|---|
| 100 |
len_values = len(values) |
|---|
| 101 |
if self.reversed: |
|---|
| 102 |
# From http://www.python.org/doc/current/tut/node11.html |
|---|
| 103 |
def reverse(data): |
|---|
| 104 |
for index in range(len(data)-1, -1, -1): |
|---|
| 105 |
yield data[index] |
|---|
| 106 |
values = reverse(values) |
|---|
| 107 |
for i, item in enumerate(values): |
|---|
| 108 |
context['forloop'] = { |
|---|
| 109 |
# shortcuts for current loop iteration number |
|---|
| 110 |
'counter0': i, |
|---|
| 111 |
'counter': i+1, |
|---|
| 112 |
# reverse counter iteration numbers |
|---|
| 113 |
'revcounter': len_values - i, |
|---|
| 114 |
'revcounter0': len_values - i - 1, |
|---|
| 115 |
# boolean values designating first and last times through loop |
|---|
| 116 |
'first': (i == 0), |
|---|
| 117 |
'last': (i == len_values - 1), |
|---|
| 118 |
'parentloop': parentloop, |
|---|
| 119 |
} |
|---|
| 120 |
context[self.loopvar] = item |
|---|
| 121 |
for node in self.nodelist_loop: |
|---|
| 122 |
nodelist.append(node.render(context)) |
|---|
| 123 |
context.pop() |
|---|
| 124 |
return nodelist.render(context) |
|---|
| 125 |
|
|---|
| 126 |
class IfChangedNode(Node): |
|---|
| 127 |
def __init__(self, nodelist, *varlist): |
|---|
| 128 |
self.nodelist = nodelist |
|---|
| 129 |
self._last_seen = None |
|---|
| 130 |
self._varlist = varlist |
|---|
| 131 |
|
|---|
| 132 |
def render(self, context): |
|---|
| 133 |
if context.has_key('forloop') and context['forloop']['first']: |
|---|
| 134 |
self._last_seen = None |
|---|
| 135 |
try: |
|---|
| 136 |
if self._varlist: |
|---|
| 137 |
# Consider multiple parameters. |
|---|
| 138 |
# This automatically behaves like a OR evaluation of the multiple variables. |
|---|
| 139 |
compare_to = [resolve_variable(var, context) for var in self._varlist] |
|---|
| 140 |
else: |
|---|
| 141 |
compare_to = self.nodelist.render(context) |
|---|
| 142 |
except VariableDoesNotExist: |
|---|
| 143 |
compare_to = None |
|---|
| 144 |
|
|---|
| 145 |
if compare_to != self._last_seen: |
|---|
| 146 |
firstloop = (self._last_seen == None) |
|---|
| 147 |
self._last_seen = compare_to |
|---|
| 148 |
context.push() |
|---|
| 149 |
context['ifchanged'] = {'firstloop': firstloop} |
|---|
| 150 |
content = self.nodelist.render(context) |
|---|
| 151 |
context.pop() |
|---|
| 152 |
return content |
|---|
| 153 |
else: |
|---|
| 154 |
return '' |
|---|
| 155 |
|
|---|
| 156 |
class IfEqualNode(Node): |
|---|
| 157 |
def __init__(self, var1, var2, nodelist_true, nodelist_false, negate): |
|---|
| 158 |
self.var1, self.var2 = var1, var2 |
|---|
| 159 |
self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false |
|---|
| 160 |
self.negate = negate |
|---|
| 161 |
|
|---|
| 162 |
def __repr__(self): |
|---|
| 163 |
return "<IfEqualNode>" |
|---|
| 164 |
|
|---|
| 165 |
def render(self, context): |
|---|
| 166 |
try: |
|---|
| 167 |
val1 = resolve_variable(self.var1, context) |
|---|
| 168 |
except VariableDoesNotExist: |
|---|
| 169 |
val1 = None |
|---|
| 170 |
try: |
|---|
| 171 |
val2 = resolve_variable(self.var2, context) |
|---|
| 172 |
except VariableDoesNotExist: |
|---|
| 173 |
val2 = None |
|---|
| 174 |
if (self.negate and val1 != val2) or (not self.negate and val1 == val2): |
|---|
| 175 |
return self.nodelist_true.render(context) |
|---|
| 176 |
return self.nodelist_false.render(context) |
|---|
| 177 |
|
|---|
| 178 |
class IfNode(Node): |
|---|
| 179 |
def __init__(self, bool_exprs, nodelist_true, nodelist_false, link_type): |
|---|
| 180 |
self.bool_exprs = bool_exprs |
|---|
| 181 |
self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false |
|---|
| 182 |
self.link_type = link_type |
|---|
| 183 |
|
|---|
| 184 |
def __repr__(self): |
|---|
| 185 |
return "<If node>" |
|---|
| 186 |
|
|---|
| 187 |
def __iter__(self): |
|---|
| 188 |
for node in self.nodelist_true: |
|---|
| 189 |
yield node |
|---|
| 190 |
for node in self.nodelist_false: |
|---|
| 191 |
yield node |
|---|
| 192 |
|
|---|
| 193 |
def get_nodes_by_type(self, nodetype): |
|---|
| 194 |
nodes = [] |
|---|
| 195 |
if isinstance(self, nodetype): |
|---|
| 196 |
nodes.append(self) |
|---|
| 197 |
nodes.extend(self.nodelist_true.get_nodes_by_type(nodetype)) |
|---|
| 198 |
nodes.extend(self.nodelist_false.get_nodes_by_type(nodetype)) |
|---|
| 199 |
return nodes |
|---|
| 200 |
|
|---|
| 201 |
def render(self, context): |
|---|
| 202 |
if self.link_type == IfNode.LinkTypes.or_: |
|---|
| 203 |
for ifnot, bool_expr in self.bool_exprs: |
|---|
| 204 |
try: |
|---|
| 205 |
value = bool_expr.resolve(context, True) |
|---|
| 206 |
except VariableDoesNotExist: |
|---|
| 207 |
value = None |
|---|
| 208 |
if (value and not ifnot) or (ifnot and not value): |
|---|
| 209 |
return self.nodelist_true.render(context) |
|---|
| 210 |
return self.nodelist_false.render(context) |
|---|
| 211 |
else: |
|---|
| 212 |
for ifnot, bool_expr in self.bool_exprs: |
|---|
| 213 |
try: |
|---|
| 214 |
value = bool_expr.resolve(context, True) |
|---|
| 215 |
except VariableDoesNotExist: |
|---|
| 216 |
value = None |
|---|
| 217 |
if not ((value and not ifnot) or (ifnot and not value)): |
|---|
| 218 |
return self.nodelist_false.render(context) |
|---|
| 219 |
return self.nodelist_true.render(context) |
|---|
| 220 |
|
|---|
| 221 |
class LinkTypes: |
|---|
| 222 |
and_ = 0, |
|---|
| 223 |
or_ = 1 |
|---|
| 224 |
|
|---|
| 225 |
class RegroupNode(Node): |
|---|
| 226 |
def __init__(self, target, expression, var_name): |
|---|
| 227 |
self.target, self.expression = target, expression |
|---|
| 228 |
self.var_name = var_name |
|---|
| 229 |
|
|---|
| 230 |
def render(self, context): |
|---|
| 231 |
obj_list = self.target.resolve(context, True) |
|---|
| 232 |
if obj_list == None: # target_var wasn't found in context; fail silently |
|---|
| 233 |
context[self.var_name] = [] |
|---|
| 234 |
return '' |
|---|
| 235 |
output = [] # list of dictionaries in the format {'grouper': 'key', 'list': [list of contents]} |
|---|
| 236 |
for obj in obj_list: |
|---|
| 237 |
grouper = self.expression.resolve(Context({'var': obj}), True) |
|---|
| 238 |
# TODO: Is this a sensible way to determine equality? |
|---|
| 239 |
if output and repr(output[-1]['grouper']) == repr(grouper): |
|---|
| 240 |
output[-1]['list'].append(obj) |
|---|
| 241 |
else: |
|---|
| 242 |
output.append({'grouper': grouper, 'list': [obj]}) |
|---|
| 243 |
context[self.var_name] = output |
|---|
| 244 |
return '' |
|---|
| 245 |
|
|---|
| 246 |
def include_is_allowed(filepath): |
|---|
| 247 |
for root in settings.ALLOWED_INCLUDE_ROOTS: |
|---|
| 248 |
if filepath.startswith(root): |
|---|
| 249 |
return True |
|---|
| 250 |
return False |
|---|
| 251 |
|
|---|
| 252 |
class SsiNode(Node): |
|---|
| 253 |
def __init__(self, filepath, parsed): |
|---|
| 254 |
self.filepath, self.parsed = filepath, parsed |
|---|
| 255 |
|
|---|
| 256 |
def render(self, context): |
|---|
| 257 |
if not include_is_allowed(self.filepath): |
|---|
| 258 |
if settings.DEBUG: |
|---|
| 259 |
return "[Didn't have permission to include file]" |
|---|
| 260 |
else: |
|---|
| 261 |
return '' # Fail silently for invalid includes. |
|---|
| 262 |
try: |
|---|
| 263 |
fp = open(self.filepath, 'r') |
|---|
| 264 |
output = fp.read() |
|---|
| 265 |
fp.close() |
|---|
| 266 |
except IOError: |
|---|
| 267 |
output = '' |
|---|
| 268 |
if self.parsed: |
|---|
| 269 |
try: |
|---|
| 270 |
t = Template(output, name=self.filepath) |
|---|
| 271 |
return t.render(context) |
|---|
| 272 |
except TemplateSyntaxError, e: |
|---|
| 273 |
if settings.DEBUG: |
|---|
| 274 |
return "[Included template had syntax error: %s]" % e |
|---|
| 275 |
else: |
|---|
| 276 |
return '' # Fail silently for invalid included templates. |
|---|
| 277 |
return output |
|---|
| 278 |
|
|---|
| 279 |
class LoadNode(Node): |
|---|
| 280 |
def render(self, context): |
|---|
| 281 |
return '' |
|---|
| 282 |
|
|---|
| 283 |
class NowNode(Node): |
|---|
| 284 |
def __init__(self, format_string): |
|---|
| 285 |
self.format_string = format_string |
|---|
| 286 |
|
|---|
| 287 |
def render(self, context): |
|---|
| 288 |
from datetime import datetime |
|---|
| 289 |
from django.utils.dateformat import DateFormat |
|---|
| 290 |
df = DateFormat(datetime.now()) |
|---|
| 291 |
return df.format(self.format_string) |
|---|
| 292 |
|
|---|
| 293 |
class SpacelessNode(Node): |
|---|
| 294 |
def __init__(self, nodelist): |
|---|
| 295 |
self.nodelist = nodelist |
|---|
| 296 |
|
|---|
| 297 |
def render(self, context): |
|---|
| 298 |
from django.utils.html import strip_spaces_between_tags |
|---|
| 299 |
return strip_spaces_between_tags(self.nodelist.render(context).strip()) |
|---|
| 300 |
|
|---|
| 301 |
class TemplateTagNode(Node): |
|---|
| 302 |
mapping = {'openblock': BLOCK_TAG_START, |
|---|
| 303 |
'closeblock': BLOCK_TAG_END, |
|---|
| 304 |
'openvariable': VARIABLE_TAG_START, |
|---|
| 305 |
'closevariable': VARIABLE_TAG_END, |
|---|
| 306 |
'openbrace': SINGLE_BRACE_START, |
|---|
| 307 |
'closebrace': SINGLE_BRACE_END, |
|---|
| 308 |
'opencomment': COMMENT_TAG_START, |
|---|
| 309 |
'closecomment': COMMENT_TAG_END, |
|---|
| 310 |
} |
|---|
| 311 |
|
|---|
| 312 |
def __init__(self, tagtype): |
|---|
| 313 |
self.tagtype = tagtype |
|---|
| 314 |
|
|---|
| 315 |
def render(self, context): |
|---|
| 316 |
return self.mapping.get(self.tagtype, '') |
|---|
| 317 |
|
|---|
| 318 |
class URLNode(Node): |
|---|
| 319 |
def __init__(self, view_name, args, kwargs): |
|---|
| 320 |
self.view_name = view_name |
|---|
| 321 |
self.args = args |
|---|
| 322 |
self.kwargs = kwargs |
|---|
| 323 |
|
|---|
| 324 |
def render(self, context): |
|---|
| 325 |
from django.core.urlresolvers import reverse, NoReverseMatch |
|---|
| 326 |
args = [arg.resolve(context) for arg in self.args] |
|---|
| 327 |
kwargs = dict([(k, v.resolve(context)) for k, v in self.kwargs.items()]) |
|---|
| 328 |
try: |
|---|
| 329 |
return reverse(self.view_name, args=args, kwargs=kwargs) |
|---|
| 330 |
except NoReverseMatch: |
|---|
| 331 |
try: |
|---|
| 332 |
project_name = settings.SETTINGS_MODULE.split('.')[0] |
|---|
| 333 |
return reverse(project_name + '.' + self.view_name, args=args, kwargs=kwargs) |
|---|
| 334 |
except NoReverseMatch: |
|---|
| 335 |
return '' |
|---|
| 336 |
|
|---|
| 337 |
class WidthRatioNode(Node): |
|---|
| 338 |
def __init__(self, val_expr, max_expr, max_width): |
|---|
| 339 |
self.val_expr = val_expr |
|---|
| 340 |
self.max_expr = max_expr |
|---|
| 341 |
self.max_width = max_width |
|---|
| 342 |
|
|---|
| 343 |
def render(self, context): |
|---|
| 344 |
try: |
|---|
| 345 |
value = self.val_expr.resolve(context) |
|---|
| 346 |
maxvalue = self.max_expr.resolve(context) |
|---|
| 347 |
except VariableDoesNotExist: |
|---|
| 348 |
return '' |
|---|
| 349 |
try: |
|---|
| 350 |
value = float(value) |
|---|
| 351 |
maxvalue = float(maxvalue) |
|---|
| 352 |
ratio = (value / maxvalue) * int(self.max_width) |
|---|
| 353 |
except (ValueError, ZeroDivisionError): |
|---|
| 354 |
return '' |
|---|
| 355 |
return str(int(round(ratio))) |
|---|
| 356 |
|
|---|
| 357 |
#@register.tag |
|---|
| 358 |
def comment(parser, token): |
|---|
| 359 |
""" |
|---|
| 360 |
Ignore everything between ``{% comment %}`` and ``{% endcomment %}`` |
|---|
| 361 |
""" |
|---|
| 362 |
parser.skip_past('endcomment') |
|---|
| 363 |
return CommentNode() |
|---|
| 364 |
comment = register.tag(comment) |
|---|
| 365 |
|
|---|
| 366 |
#@register.tag |
|---|
| 367 |
def cycle(parser, token): |
|---|
| 368 |
""" |
|---|
| 369 |
Cycle among the given strings each time this tag is encountered |
|---|
| 370 |
|
|---|
| 371 |
Within a loop, cycles among the given strings each time through |
|---|
| 372 |
the loop:: |
|---|
| 373 |
|
|---|
| 374 |
{% for o in some_list %} |
|---|
| 375 |
<tr class="{% cycle row1,row2 %}"> |
|---|
| 376 |
... |
|---|
| 377 |
</tr> |
|---|
| 378 |
{% endfor %} |
|---|
| 379 |
|
|---|
| 380 |
Outside of a loop, give the values a unique name the first time you call |
|---|
| 381 |
it, then use that name each sucessive time through:: |
|---|
| 382 |
|
|---|
| 383 |
<tr class="{% cycle row1,row2,row3 as rowcolors %}">...</tr> |
|---|
| 384 |
<tr class="{% cycle rowcolors %}">...</tr> |
|---|
| 385 |
<tr class="{% cycle rowcolors %}">...</tr> |
|---|
| 386 |
|
|---|
| 387 |
You can use any number of values, seperated by commas. Make sure not to |
|---|
| 388 |
put spaces between the values -- only commas. |
|---|
| 389 |
""" |
|---|
| 390 |
|
|---|
| 391 |
# Note: This returns the exact same node on each {% cycle name %} call; that |
|---|
| 392 |
# is, the node object returned from {% cycle a,b,c as name %} and the one |
|---|
| 393 |
# returned from {% cycle name %} are the exact same object. This shouldn't |
|---|
| 394 |
# cause problems (heh), but if it does, now you know. |
|---|
| 395 |
# |
|---|
| 396 |
# Ugly hack warning: this stuffs the named template dict into parser so |
|---|
| 397 |
# that names are only unique within each template (as opposed to using |
|---|
| 398 |
# a global variable, which would make cycle names have to be unique across |
|---|
| 399 |
# *all* templates. |
|---|
| 400 |
|
|---|
| 401 |
args = token.contents.split() |
|---|
| 402 |
if len(args) < 2: |
|---|
| 403 |
raise TemplateSyntaxError("'Cycle' statement requires at least two arguments") |
|---|
| 404 |
|
|---|
| 405 |
elif len(args) == 2 and "," in args[1]: |
|---|
| 406 |
# {% cycle a,b,c %} |
|---|
| 407 |
cyclevars = [v for v in args[1].split(",") if v] # split and kill blanks |
|---|
| 408 |
return CycleNode(cyclevars) |
|---|
| 409 |
# {% cycle name %} |
|---|
| 410 |
|
|---|
| 411 |
elif len(args) == 2: |
|---|
| 412 |
name = args[1] |
|---|
| 413 |
if not hasattr(parser, '_namedCycleNodes'): |
|---|
| 414 |
raise TemplateSyntaxError("No named cycles in template: '%s' is not defined" % name) |
|---|
| 415 |
if not parser._namedCycleNodes.has_key(name): |
|---|
| 416 |
raise TemplateSyntaxError("Named cycle '%s' does not exist" % name) |
|---|
| 417 |
return parser._namedCycleNodes[name] |
|---|
| 418 |
|
|---|
| 419 |
elif len(args) == 4: |
|---|
| 420 |
# {% cycle a,b,c as name %} |
|---|
| 421 |
if args[2] != 'as': |
|---|
| 422 |
raise TemplateSyntaxError("Second 'cycle' argument must be 'as'") |
|---|
| 423 |
cyclevars = [v for v in args[1].split(",") if v] # split and kill blanks |
|---|
| 424 |
name = args[3] |
|---|
| 425 |
node = CycleNode(cyclevars, name) |
|---|
| 426 |
|
|---|
| 427 |
if not hasattr(parser, '_namedCycleNodes'): |
|---|
| 428 |
parser._namedCycleNodes = {} |
|---|
| 429 |
|
|---|
| 430 |
parser._namedCycleNodes[name] = node |
|---|
| 431 |
return node |
|---|
| 432 |
|
|---|
| 433 |
else: |
|---|
| 434 |
raise TemplateSyntaxError("Invalid arguments to 'cycle': %s" % args) |
|---|
| 435 |
cycle = register.tag(cycle) |
|---|
| 436 |
|
|---|
| 437 |
def debug(parser, token): |
|---|
| 438 |
""" |
|---|
| 439 |
Output a whole load of debugging information, including the current context and imported modules. |
|---|
| 440 |
|
|---|
| 441 |
Sample usage:: |
|---|
| 442 |
|
|---|
| 443 |
<pre> |
|---|
| 444 |
{% debug %} |
|---|
| 445 |
</pre> |
|---|
| 446 |
""" |
|---|
| 447 |
return DebugNode() |
|---|
| 448 |
debug = register.tag(debug) |
|---|
| 449 |
|
|---|
| 450 |
#@register.tag(name="filter") |
|---|
| 451 |
def do_filter(parser, token): |
|---|
| 452 |
""" |
|---|
| 453 |
Filter the contents of the blog through variable filters. |
|---|
| 454 |
|
|---|
| 455 |
Filters can also be piped through each other, and they can have |
|---|
| 456 |
arguments -- just like in variable syntax. |
|---|
| 457 |
|
|---|
| 458 |
Sample usage:: |
|---|
| 459 |
|
|---|
| 460 |
{% filter escape|lower %} |
|---|
| 461 |
This text will be HTML-escaped, and will appear in lowercase. |
|---|
| 462 |
{% endfilter %} |
|---|
| 463 |
""" |
|---|
| 464 |
_, rest = token.contents.split(None, 1) |
|---|
| 465 |
filter_expr = parser.compile_filter("var|%s" % (rest)) |
|---|
| 466 |
nodelist = parser.parse(('endfilter',)) |
|---|
| 467 |
parser.delete_first_token() |
|---|
| 468 |
return FilterNode(filter_expr, nodelist) |
|---|
| 469 |
filter = register.tag("filter", do_filter) |
|---|
| 470 |
|
|---|
| 471 |
#@register.tag |
|---|
| 472 |
def firstof(parser, token): |
|---|
| 473 |
""" |
|---|
| 474 |
Outputs the first variable passed that is not False. |
|---|
| 475 |
|
|---|
| 476 |
Outputs nothing if all the passed variables are False. |
|---|
| 477 |
|
|---|
| 478 |
Sample usage:: |
|---|
| 479 |
|
|---|
| 480 |
{% firstof var1 var2 var3 %} |
|---|
| 481 |
|
|---|
| 482 |
This is equivalent to:: |
|---|
| 483 |
|
|---|
| 484 |
{% if var1 %} |
|---|
| 485 |
{{ var1 }} |
|---|
| 486 |
{% else %}{% if var2 %} |
|---|
| 487 |
{{ var2 }} |
|---|
| 488 |
{% else %}{% if var3 %} |
|---|
| 489 |
{{ var3 }} |
|---|
| 490 |
{% endif %}{% endif %}{% endif %} |
|---|
| 491 |
|
|---|
| 492 |
but obviously much cleaner! |
|---|
| 493 |
""" |
|---|
| 494 |
bits = token.contents.split()[1:] |
|---|
| 495 |
if len(bits) < 1: |
|---|
| 496 |
raise TemplateSyntaxError, "'firstof' statement requires at least one argument" |
|---|
| 497 |
return FirstOfNode(bits) |
|---|
| 498 |
firstof = register.tag(firstof) |
|---|
| 499 |
|
|---|
| 500 |
#@register.tag(name="for") |
|---|
| 501 |
def do_for(parser, token): |
|---|
| 502 |
""" |
|---|
| 503 |
Loop over each item in an array. |
|---|
| 504 |
|
|---|
| 505 |
For example, to display a list of athletes given ``athlete_list``:: |
|---|
| 506 |
|
|---|
| 507 |
<ul> |
|---|
| 508 |
{% for athlete in athlete_list %} |
|---|
| 509 |
<li>{{ athlete.name }}</li> |
|---|
| 510 |
{% endfor %} |
|---|
| 511 |
</ul> |
|---|
| 512 |
|
|---|
| 513 |
You can also loop over a list in reverse by using |
|---|
| 514 |
``{% for obj in list reversed %}``. |
|---|
| 515 |
|
|---|
| 516 |
The for loop sets a number of variables available within the loop: |
|---|
| 517 |
|
|---|
| 518 |
========================== ================================================ |
|---|
| 519 |
Variable Description |
|---|
| 520 |
========================== ================================================ |
|---|
| 521 |
``forloop.counter`` The current iteration of the loop (1-indexed) |
|---|
| 522 |
``forloop.counter0`` The current iteration of the loop (0-indexed) |
|---|
| 523 |
``forloop.revcounter`` The number of iterations from the end of the |
|---|
| 524 |
loop (1-indexed) |
|---|
| 525 |
``forloop.revcounter0`` The number of iterations from the end of the |
|---|
| 526 |
loop (0-indexed) |
|---|
| 527 |
``forloop.first`` True if this is the first time through the loop |
|---|
| 528 |
``forloop.last`` True if this is the last time through the loop |
|---|
| 529 |
``forloop.parentloop`` For nested loops, this is the loop "above" the |
|---|
| 530 |
current one |
|---|
| 531 |
========================== ================================================ |
|---|
| 532 |
|
|---|
| 533 |
""" |
|---|
| 534 |
bits = token.contents.split() |
|---|
| 535 |
if len(bits) == 5 and bits[4] != 'reversed': |
|---|
| 536 |
raise TemplateSyntaxError, "'for' statements with five words should end in 'reversed': %s" % token.contents |
|---|
| 537 |
if len(bits) not in (4, 5): |
|---|
| 538 |
raise TemplateSyntaxError, "'for' statements should have either four or five words: %s" % token.contents |
|---|
| 539 |
if bits[2] != 'in& |
|---|