Ticket #5908: 5908-resetcycle.3.diff

File 5908-resetcycle.3.diff, 7.3 KB (added by jamesp, 4 years ago)

Updated patch to current trunk; added TemplateSyntaxError tests

  • django/template/defaulttags.py

    diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
    index 06838cf..4b1f8ce 100644
    a b class CycleNode(Node): 
    111111        self.variable_name = variable_name
    112112        self.silent = silent
    113113
     114    def reset(self, context):
     115        context.render_context[self] = itertools_cycle(self.cyclevars)
     116
    114117    def render(self, context):
    115118        if self not in context.render_context:
    116119            # First time the node is rendered in template
    class CycleNode(Node): 
    123126            return ''
    124127        return value
    125128
     129class ResetCycleNode(Node):
     130    def __init__(self, node):
     131        self.node = node
     132
     133    def render(self, context):
     134        self.node.reset(context)
     135        return ''
     136
    126137class DebugNode(Node):
    127138    def render(self, context):
    128139        from pprint import pformat
    def cycle(parser, token): 
    582593    # Ugly hack warning: This stuffs the named template dict into parser so
    583594    # that names are only unique within each template (as opposed to using
    584595    # a global variable, which would make cycle names have to be unique across
    585     # *all* templates.
     596    # *all* templates. Also keeps last unnamed node in the parser.
    586597
    587598    args = token.split_contents()
    588599
    def cycle(parser, token): 
    627638    else:
    628639        values = [parser.compile_filter(arg) for arg in args[1:]]
    629640        node = CycleNode(values)
     641        parser._lastUnnamedCycleNode = node
    630642    return node
    631643
    632644@register.tag
     645def resetcycle(parser, token):
     646    """
     647    Resets a cycle tag.
     648
     649    If an argument is given, resets the last rendered cycle tag whose name
     650    matches the argument, else resets last rendered unnamed cycle tag.
     651
     652    """
     653    args = token.split_contents()
     654
     655    if len(args) > 2:
     656        raise TemplateSyntaxError("%r tag accepts at most one argument."
     657                                  % args[0])
     658    if len(args) == 2:
     659        name = args[1]
     660        try:
     661            return ResetCycleNode(parser._namedCycleNodes[name])
     662        except AttributeError:
     663            raise TemplateSyntaxError("No named cycles in template. "
     664                                      "%r is not defined" % name)
     665        except KeyError:
     666            raise TemplateSyntaxError("Named cycle %r does not exist" % name)
     667    try:
     668        return ResetCycleNode(parser._lastUnnamedCycleNode)
     669    except AttributeError:
     670        raise TemplateSyntaxError("No unnamed cycles in template.")
     671
     672@register.tag
    633673def csrf_token(parser, token):
    634674    return CsrfTokenNode()
    635675
  • docs/ref/templates/builtins.txt

    diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt
    index 3638ce9..6f440e5 100644
    a b explicitly:: 
    144144        {% cycle var1 var2 var3 %}
    145145    {% endfilter %}
    146146
     147You can use the `resetcycle`_ tag to reset a ``{% cycle %}`` tag to restart
     148from its first value when it's next encountered.
     149
    147150For backwards compatibility, the ``{% cycle %}`` tag supports the much inferior
    148151old syntax from previous Django versions. You shouldn't use this in any new
    149152projects, but for the sake of the people who are still using it, here's what it
    attribute, allowing you to group on the display string rather than the 
    864867``{{ gender.grouper }}`` will now display the value fields from the
    865868``choices`` set rather than the keys.
    866869
     870.. templatetag:: resetcycle
     871
     872resetcycle
     873^^^^^^^^^^
     874
     875Resets a previous `cycle`_ so that it restarts from its first item at its next
     876invocation.  Without arguments, ``{% resetcycle %}`` will reset the last
     877unnamed ``{% cycle %}`` defined in the template.
     878
     879Example usage::
     880
     881    {% for coach in coach_list %}
     882        <h1>{{ coach.name }}</h1>
     883        {% for athlete in coach.athlete_set.all %}
     884            <p class="{% cycle 'odd' 'even' %}">{{ athlete.name }}</p>
     885        {% endfor %}
     886        {% resetcycle %}
     887    {% endfor %}
     888
     889The athlete list for every coach starts with ``class="odd"``.  Without the
     890``{% resetcycle %}`` tag, the first athlete of a coach might be rendered with
     891``class="even"`` if the last athlete of the previous coach had ``class="odd"``.
     892
     893You can also reset named cycle tags::
     894
     895    {% for item in list %}
     896        <p class="{% cycle 'odd' 'evn' as stripe %} {% cycle 'majr' 'minr' 'minr' 'minr' 'minr' as tick %}">
     897            {{ item.data }}
     898        </p>
     899        {% ifchanged item.category %}
     900            <h1>{{ item.category }}</h1>
     901            {% if not forloop.first %}{% resetcycle tick %}{% endif %}
     902        {% endifchanged %}
     903    {% endfor %}
     904
     905In this example we have both the alternating odd/even rows and a "major" row
     906every fifth row.  Only the five-row cycle is reset when a category changes.
     907
    867908.. templatetag:: spaceless
    868909
    869910spaceless
  • tests/regressiontests/templates/tests.py

    diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
    index c5c65fd..9c1d2f2 100644
    a b class Templates(unittest.TestCase): 
    750750            'included-cycle': ('{{ abc }}', {'abc': 'xxx'}, 'xxx'),
    751751            'cycle24': ("{% for x in values %}{% cycle 'a' 'b' 'c' as abc silent %}{% include 'included-cycle' %}{% endfor %}", {'values': [1,2,3,4]}, "abca"),
    752752
     753            ### RESETCYCLE TAG ########################################################
     754            'resetcycle01': ("{% resetcycle %}", {}, template.TemplateSyntaxError),
     755            'resetcycle02': ("{% resetcycle undefinedcycle %}", {}, template.TemplateSyntaxError),
     756            'resetcycle03': ("{% cycle 'a' 'b' 'c' as abc %}{% resetcycle %}", {}, template.TemplateSyntaxError),
     757            'resetcycle04': ("{% cycle 'a' 'b' 'c' %}{% resetcycle undefinedcycle %}", {}, template.TemplateSyntaxError),
     758            'resetcycle05': ("{% cycle 'a' 'b' 'c' as abc %}{% resetcycle undefinedcycle %}", {}, template.TemplateSyntaxError),
     759            'resetcycle06': ("{% for i in test %}{% cycle 'a' 'b' %}{% resetcycle %}{% endfor %}", {'test': range(5)}, 'aaaaa'),
     760            'resetcycle07': ("{% cycle 'a' 'b' 'c' as abc %}{% for i in test %}{% cycle abc %}{% cycle '-' '+' %}{% resetcycle %}{% endfor %}", {'test': range(5)}, 'ab-c-a-b-c-'),
     761            '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-'),
     762            'resetcycle09': ("{% for i in outer %}{% for j in inner %}{% cycle 'a' 'b' %}{% endfor %}{% resetcycle %}{% endfor %}", {'outer': range(2), 'inner': range(3)}, 'abaaba'),
     763            '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'),
     764            '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'),
     765            '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'),
     766
    753767            ### EXCEPTIONS ############################################################
    754768
    755769            # Raise exception for invalid template name
Back to Top