Ticket #5908: 5908-resetcycle.4.diff
File 5908-resetcycle.4.diff, 7.7 KB (added by , 12 years ago) |
---|
-
django/template/defaulttags.py
diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index 04e7a37..b84ed4e 100644
a b class CycleNode(Node): 65 65 self.variable_name = variable_name 66 66 self.silent = silent 67 67 self.escape = escape # only while the "future" version exists 68 69 def reset(self, context): 70 context.render_context[self] = itertools_cycle(self.cyclevars) 68 71 69 72 def render(self, context): 70 73 if self not in context.render_context: … … class CycleNode(Node): 80 83 value = mark_safe(value) 81 84 return render_value_in_context(value, context) 82 85 86 class ResetCycleNode(Node): 87 def __init__(self, node): 88 self.node = node 89 def render(self, context): 90 self.node.reset(context) 91 return '' 92 83 93 class DebugNode(Node): 84 94 def render(self, context): 85 95 from pprint import pformat … … def cycle(parser, token, escape=False): 578 588 # Ugly hack warning: This stuffs the named template dict into parser so 579 589 # that names are only unique within each template (as opposed to using 580 590 # a global variable, which would make cycle names have to be unique across 581 # *all* templates. 591 # *all* templates. Also keeps last unnamed node in the parser. 582 592 583 593 args = token.split_contents() 584 594 … … def cycle(parser, token, escape=False): 623 633 else: 624 634 values = [parser.compile_filter(arg) for arg in args[1:]] 625 635 node = CycleNode(values, escape=escape) 636 parser._lastUnnamedCycleNode = node 626 637 return node 638 639 @register.tag 640 def resetcycle(parser, token): 641 """ 642 Resets a cycle tag. 643 644 If an argument is given, resets the last rendered cycle tag whose name 645 matches the argument, else resets last rendered unnamed cycle tag. 646 647 """ 648 args = token.split_contents() 649 650 if len(args) > 2: 651 raise TemplateSyntaxError("%r tag accepts at most one argument." 652 % args[0]) 653 if len(args) == 2: 654 name = args[1] 655 try: 656 return ResetCycleNode(parser._namedCycleNodes[name]) 657 except AttributeError: 658 raise TemplateSyntaxError("No named cycles in template. " 659 "%r is not defined" % name) 660 except KeyError: 661 raise TemplateSyntaxError("Named cycle %r does not exist" % name) 662 try: 663 return ResetCycleNode(parser._lastUnnamedCycleNode) 664 except AttributeError: 665 raise TemplateSyntaxError("No unnamed cycles in template.") 627 666 628 667 @register.tag 629 668 def csrf_token(parser, token): -
docs/ref/templates/builtins.txt
diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt index 287fd4f..86f5d0f 100644
a b string literals, while values without quotes are treated as template variables. 149 149 150 150 Note that currently the variables included in the cycle will not be escaped. 151 151 Any HTML or Javascript code contained in the printed variable will be rendered 152 as-is, which could potentially lead to security issues. 152 as-is, which could potentially lead to security issues.86 153 154 You can use the `resetcycle`_ tag to reset a ``{% cycle %}`` tag to restart 155 from its first value when it's next encountered. 153 156 154 157 For backwards compatibility, the ``{% cycle %}`` tag supports the much inferior 155 158 old syntax from previous Django versions. You shouldn't use this in any new … … attribute, allowing you to group on the display string rather than the 893 896 ``{{ country.grouper }}`` will now display the value fields from the 894 897 ``choices`` set rather than the keys. 895 898 899 .. templatetag:: resetcycle 900 901 resetcycle 902 ^^^^^^^^^^ 903 904 Resets a previous `cycle`_ so that it restarts from its first item at its next 905 invocation. Without arguments, ``{% resetcycle %}`` will reset the last 906 unnamed ``{% cycle %}`` defined in the template. 907 908 Example usage:: 909 910 {% for coach in coach_list %} 911 <h1>{{ coach.name }}</h1> 912 {% for athlete in coach.athlete_set.all %} 913 <p class="{% cycle 'odd' 'even' %}">{{ athlete.name }}</p> 914 {% endfor %} 915 {% resetcycle %} 916 {% endfor %} 917 918 The athlete list for every coach starts with ``class="odd"``. Without the 919 ``{% resetcycle %}`` tag, the first athlete of a coach might be rendered with 920 ``class="even"`` if the last athlete of the previous coach had ``class="odd"``. 921 922 You can also reset named cycle tags:: 923 924 {% for item in list %} 925 <p class="{% cycle 'odd' 'evn' as stripe %} {% cycle 'majr' 'minr' 'minr' 'minr' 'minr' as tick %}"> 926 {{ item.data }} 927 </p> 928 {% ifchanged item.category %} 929 <h1>{{ item.category }}</h1> 930 {% if not forloop.first %}{% resetcycle tick %}{% endif %} 931 {% endifchanged %} 932 {% endfor %} 933 934 In this example we have both the alternating odd/even rows and a "major" row 935 every fifth row. Only the five-row cycle is reset when a category changes. 936 896 937 .. templatetag:: spaceless 897 938 898 939 spaceless -
tests/template_tests/tests.py
diff --git a/tests/template_tests/tests.py b/tests/template_tests/tests.py index 2aeaee9..c2b3a76 100644
a b class Templates(TestCase): 818 818 'cycle26': ('{% load cycle from future %}{% cycle a b as ab %}{% cycle ab %}', {'a': '<', 'b': '>'}, '<>'), 819 819 'cycle27': ('{% load cycle from future %}{% autoescape off %}{% cycle a b as ab %}{% cycle ab %}{% endautoescape %}', {'a': '<', 'b': '>'}, '<>'), 820 820 'cycle28': ('{% load cycle from future %}{% cycle a|safe b as ab %}{% cycle ab %}', {'a': '<', 'b': '>'}, '<>'), 821 822 ### RESETCYCLE TAG ######################################################## 823 'resetcycle01': ("{% resetcycle %}", {}, template.TemplateSyntaxError), 824 'resetcycle02': ("{% resetcycle undefinedcycle %}", {}, template.TemplateSyntaxError), 825 'resetcycle03': ("{% cycle 'a' 'b' 'c' as abc %}{% resetcycle %}", {}, template.TemplateSyntaxError), 826 'resetcycle04': ("{% cycle 'a' 'b' 'c' %}{% resetcycle undefinedcycle %}", {}, template.TemplateSyntaxError), 827 'resetcycle05': ("{% cycle 'a' 'b' 'c' as abc %}{% resetcycle undefinedcycle %}", {}, template.TemplateSyntaxError), 828 'resetcycle06': ("{% for i in test %}{% cycle 'a' 'b' %}{% resetcycle %}{% endfor %}", {'test': range(5)}, 'aaaaa'), 829 'resetcycle07': ("{% cycle 'a' 'b' 'c' as abc %}{% for i in test %}{% cycle abc %}{% cycle '-' '+' %}{% resetcycle %}{% endfor %}", {'test': range(5)}, 'ab-c-a-b-c-'), 830 'resetcycle08': ("{% cycle 'a' 'b' 'c' as abc %}{% for i in test %}{% resetcycle abc %}{% cycle abc %}{% cycle '-' '+' %}{% endfor %}", {'test': range(5)}, 'aa-a+a-a+a-'), 831 'resetcycle09': ("{% for i in outer %}{% for j in inner %}{% cycle 'a' 'b' %}{% endfor %}{% resetcycle %}{% endfor %}", {'outer': range(2), 'inner': range(3)}, 'abaaba'), 832 'resetcycle10': ("{% for i in outer %}{% cycle 'a' 'b' %}{% for j in inner %}{% cycle 'X' 'Y' %}{% endfor %}{% resetcycle %}{% endfor %}", {'outer': range(2), 'inner': range(3)}, 'aXYXbXYX'), 833 'resetcycle11': ("{% for i in test %}{% cycle 'X' 'Y' 'Z' as XYZ %}{% cycle 'a' 'b' 'c' as abc %}{% ifequal i 1 %}{% resetcycle abc %}{% endifequal %}{% endfor %}", {'test': range(5)}, 'XaYbZaXbYc'), 834 'resetcycle12': ("{% for i in test %}{% cycle 'X' 'Y' 'Z' as XYZ %}{% cycle 'a' 'b' 'c' as abc %}{% ifequal i 1 %}{% resetcycle XYZ %}{% endifequal %}{% endfor %}", {'test': range(5)}, 'XaYbXcYaZb'), 821 835 822 836 ### EXCEPTIONS ############################################################ 823 837