Ticket #3523: unpacking-for.diff
File unpacking-for.diff, 9.0 KB (added by , 17 years ago) |
---|
-
django/template/defaulttags.py
5 5 from django.template import get_library, Library, InvalidTemplateLibrary 6 6 from django.conf import settings 7 7 import sys 8 import re 8 9 9 10 register = Library() 10 11 … … 61 62 return '' 62 63 63 64 class ForNode(Node): 64 def __init__(self, loopvar , sequence, reversed, nodelist_loop):65 self.loopvar , self.sequence = loopvar, sequence65 def __init__(self, loopvars, sequence, reversed, nodelist_loop): 66 self.loopvars, self.sequence = loopvars, sequence 66 67 self.reversed = reversed 67 68 self.nodelist_loop = nodelist_loop 68 69 … … 72 73 else: 73 74 reversed = '' 74 75 return "<For Node: for %s in %s, tail_len: %d%s>" % \ 75 ( self.loopvar, self.sequence, len(self.nodelist_loop), reversed)76 (', '.join( self.loopvars ), self.sequence, len(self.nodelist_loop), reversed) 76 77 77 78 def __iter__(self): 78 79 for node in self.nodelist_loop: … … 107 108 for index in range(len(data)-1, -1, -1): 108 109 yield data[index] 109 110 values = reverse(values) 111 unpack = len(self.loopvars) > 1 110 112 for i, item in enumerate(values): 111 113 context['forloop'] = { 112 114 # shortcuts for current loop iteration number … … 120 122 'last': (i == len_values - 1), 121 123 'parentloop': parentloop, 122 124 } 123 context[self.loopvar] = item 125 if unpack: 126 # If there are multiple loop variables, unpack the item into them. 127 context.update(dict(zip(self.loopvars, item))) 128 else: 129 context[self.loopvars[0]] = item 124 130 for node in self.nodelist_loop: 125 131 nodelist.append(node.render(context)) 132 if unpack: 133 # The loop variables were pushed on to the context so pop them 134 # off again. This is necessary because the tag lets the length 135 # of loopvars differ to the length of each set of items and we 136 # don't want to leave any vars from the previous loop on the 137 # context. 138 context.pop() 126 139 context.pop() 127 140 return nodelist.render(context) 128 141 … … 486 499 nodelist = parser.parse(('endfilter',)) 487 500 parser.delete_first_token() 488 501 return FilterNode(filter_expr, nodelist) 489 filter = register.tag("filter", do_filter)502 do_filter = register.tag("filter", do_filter) 490 503 491 504 #@register.tag 492 505 def firstof(parser, token): … … 530 543 {% endfor %} 531 544 </ul> 532 545 533 You can alsoloop over a list in reverse by using546 You can loop over a list in reverse by using 534 547 ``{% for obj in list reversed %}``. 548 549 You can also unpack multiple values from a two-dimensional array:: 550 551 {% for key,value in dict.items %} 552 {{ key }}: {{ value }} 553 {% endfor %} 535 554 536 555 The for loop sets a number of variables available within the loop: 537 556 … … 552 571 553 572 """ 554 573 bits = token.contents.split() 555 if len(bits) == 5 and bits[4] != 'reversed': 556 raise TemplateSyntaxError, "'for' statements with five words should end in 'reversed': %s" % token.contents 557 if len(bits) not in (4, 5): 558 raise TemplateSyntaxError, "'for' statements should have either four or five words: %s" % token.contents 559 if bits[2] != 'in': 560 raise TemplateSyntaxError, "'for' statement must contain 'in' as the second word: %s" % token.contents 561 loopvar = bits[1] 562 sequence = parser.compile_filter(bits[3]) 563 reversed = (len(bits) == 5) 574 if len(bits) < 4: 575 raise TemplateSyntaxError, "'for' statements should have at least four words: %s" % token.contents 576 577 reversed = bits[-1] == 'reversed' 578 in_index = reversed and -3 or -2 579 if bits[in_index] != 'in': 580 raise TemplateSyntaxError, "'for' statements should use the format 'for x in y': %s" % token.contents 581 582 loopvars = re.sub(r' *, *', ',', ' '.join(bits[1:in_index])).split(',') 583 for var in loopvars: 584 if not var or ' ' in var: 585 raise TemplateSyntaxError, "'for' tag received an invalid argument: %s" % token.contents 586 587 sequence = parser.compile_filter(bits[in_index+1]) 564 588 nodelist_loop = parser.parse(('endfor',)) 565 589 parser.delete_first_token() 566 return ForNode(loopvar , sequence, reversed, nodelist_loop)590 return ForNode(loopvars, sequence, reversed, nodelist_loop) 567 591 do_for = register.tag("for", do_for) 568 592 569 593 def do_ifequal(parser, token, negate): -
tests/regressiontests/templates/tests.py
289 289 'for-tag-vars02': ("{% for val in values %}{{ forloop.counter0 }}{% endfor %}", {"values": [6, 6, 6]}, "012"), 290 290 'for-tag-vars03': ("{% for val in values %}{{ forloop.revcounter }}{% endfor %}", {"values": [6, 6, 6]}, "321"), 291 291 'for-tag-vars04': ("{% for val in values %}{{ forloop.revcounter0 }}{% endfor %}", {"values": [6, 6, 6]}, "210"), 292 'for-tag-unpack01': ("{% for key,value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"), 293 'for-tag-unpack03': ("{% for key, value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"), 294 'for-tag-unpack04': ("{% for key , value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"), 295 'for-tag-unpack05': ("{% for key ,value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"), 296 'for-tag-unpack06': ("{% for key value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, template.TemplateSyntaxError), 297 'for-tag-unpack07': ("{% for key,,value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, template.TemplateSyntaxError), 298 'for-tag-unpack08': ("{% for key,value, in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, template.TemplateSyntaxError), 299 # Ensure that a single loopvar doesn't truncate the list in val. 300 'for-tag-unpack09': ("{% for val in items %}{{ val.0 }}:{{ val.1 }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"), 301 # Otherwise, silently truncate if the length of loopvars differs to the length of each set of items. 302 'for-tag-unpack10': ("{% for x,y in items %}{{ x }}:{{ y }}/{% endfor %}", {"items": (('one', 1, 'carrot'), ('two', 2, 'orange'))}, "one:1/two:2/"), 303 'for-tag-unpack11': ("{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, ("one:1,/two:2,/", "one:1,INVALID/two:2,INVALID/")), 304 'for-tag-unpack12': ("{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}", {"items": (('one', 1, 'carrot'), ('two', 2))}, ("one:1,carrot/two:2,/", "one:1,carrot/two:2,INVALID/")), 305 'for-tag-unpack13': ("{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}", {"items": (('one', 1, 'carrot'), ('two', 2, 'cheese'))}, ("one:1,carrot/two:2,cheese/", "one:1,carrot/two:2,cheese/")), 292 306 293 307 ### IF TAG ################################################################ 294 308 'if-tag01': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": True}, "yes"), -
docs/templates.txt
444 444 ~~~ 445 445 446 446 Loop over each item in an array. For example, to display a list of athletes 447 given ``athlete_list``::447 provided in ``athlete_list``:: 448 448 449 449 <ul> 450 450 {% for athlete in athlete_list %} … … 452 452 {% endfor %} 453 453 </ul> 454 454 455 You can alsoloop over a list in reverse by using ``{% for obj in list reversed %}``.455 You can loop over a list in reverse by using ``{% for obj in list reversed %}``. 456 456 457 If you need to loop over a list of lists, you can unpack the values 458 in each sub-list into a set of known names. For example, if your context contains 459 a list of (x,y) coordinates called ``points``, you could use the following 460 to output the list of points:: 461 462 {% for x, y in points %} 463 There is a point at {{ x }},{{ y }} 464 {% endfor %} 465 466 This can also be useful if you need to access the values in a dictionary. 467 For example, if your context contained a dictionary ``dict``, the following 468 would display the keys and values of the dictionary:: 469 470 {% for key, value in dict.items %} 471 {{ key }}: {{ value }} 472 {% endfor %} 473 457 474 The for loop sets a number of variables available within the loop: 458 475 459 476 ========================== ================================================