Ticket #5908: t5908.diff

File t5908.diff, 7.9 KB (added by Florian Apolloner, 11 years ago)
  • django/template/defaulttags.py

    diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
    index 04e7a37..b6e0ecc 100644
    a b class CycleNode(Node):  
    6666        self.silent = silent
    6767        self.escape = escape        # only while the "future" version exists
    6868
     69    def reset(self, context):
     70        context.render_context[self] = itertools_cycle(self.cyclevars)
     71
    6972    def render(self, context):
    7073        if self not in context.render_context:
    7174            # First time the node is rendered in template
    class CycleNode(Node):  
    8083            value = mark_safe(value)
    8184        return render_value_in_context(value, context)
    8285
     86class ResetCycleNode(Node):
     87    def __init__(self, node):
     88        self.node = node
     89    def render(self, context):
     90        self.node.reset(context)
     91        return ''
     92
    8393class DebugNode(Node):
    8494    def render(self, context):
    8595        from pprint import pformat
    def cycle(parser, token, escape=False):  
    578588    # Ugly hack warning: This stuffs the named template dict into parser so
    579589    # that names are only unique within each template (as opposed to using
    580590    # a global variable, which would make cycle names have to be unique across
    581     # *all* templates.
     591    # *all* templates. Also keeps last node in the parser.
    582592
    583593    args = token.split_contents()
    584594
    def cycle(parser, token, escape=False):  
    623633    else:
    624634        values = [parser.compile_filter(arg) for arg in args[1:]]
    625635        node = CycleNode(values, escape=escape)
     636    parser._lastCycleNode = node
    626637    return node
    627638
    628639@register.tag
     640def 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._lastCycleNode)
     664    except AttributeError:
     665        raise TemplateSyntaxError("No cycles in template.")
     666
     667@register.tag
    629668def csrf_token(parser, token):
    630669    return CsrfTokenNode()
    631670
  • docs/ref/templates/builtins.txt

    diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt
    index 287fd4f..0df17af 100644
    a b Note that currently the variables included in the cycle will not be escaped.  
    151151Any HTML or Javascript code contained in the printed variable will be rendered
    152152as-is, which could potentially lead to security issues.
    153153
     154You can use the `resetcycle`_ tag to reset a ``{% cycle %}`` tag to restart
     155from its first value when it's next encountered.
     156
    154157For backwards compatibility, the ``{% cycle %}`` tag supports the much inferior
    155158old syntax from previous Django versions. You shouldn't use this in any new
    156159projects, 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  
    893896``{{ country.grouper }}`` will now display the value fields from the
    894897``choices`` set rather than the keys.
    895898
     899.. templatetag:: resetcycle
     900
     901resetcycle
     902^^^^^^^^^^
     903
     904Resets a previous `cycle`_ so that it restarts from its first item at its next
     905invocation.  Without arguments, ``{% resetcycle %}`` will reset the last
     906``{% cycle %}`` defined in the template.
     907
     908Example 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
     918The 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
     922You 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
     934In this example we have both the alternating odd/even rows and a "major" row
     935every fifth row.  Only the five-row cycle is reset when a category changes.
     936
    896937.. templatetag:: spaceless
    897938
    898939spaceless
  • docs/releases/1.6.txt

    diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt
    index 9888925..01b6a36 100644
    a b Minor features  
    231231  `PIL`_ is pending deprecation (support to be removed in Django 1.8).
    232232  To upgrade, you should **first** uninstall PIL, **then** install Pillow.
    233233
     234* Added a :ttag:`resetcycle` template tag which allows the user to reset the
     235  sequence of the :ttag:`cycle` template tag.
     236
    234237.. _`Pillow`: https://pypi.python.org/pypi/Pillow
    235238.. _`PIL`: https://pypi.python.org/pypi/PIL
    236239
  • tests/template_tests/tests.py

    diff --git a/tests/template_tests/tests.py b/tests/template_tests/tests.py
    index 2aeaee9..0d90394 100644
    a b class Templates(TestCase):  
    819819            'cycle27': ('{% load cycle from future %}{% autoescape off %}{% cycle a b as ab %}{% cycle ab %}{% endautoescape %}', {'a': '<', 'b': '>'}, '<>'),
    820820            'cycle28': ('{% load cycle from future %}{% cycle a|safe b as ab %}{% cycle ab %}', {'a': '<', 'b': '>'}, '<&gt;'),
    821821
     822            ### RESETCYCLE TAG ########################################################
     823            'resetcycle01': ("{% resetcycle %}", {}, template.TemplateSyntaxError),
     824            'resetcycle02': ("{% resetcycle undefinedcycle %}", {}, template.TemplateSyntaxError),
     825            'resetcycle04': ("{% cycle 'a' 'b' 'c' %}{% resetcycle undefinedcycle %}", {}, template.TemplateSyntaxError),
     826            'resetcycle05': ("{% cycle 'a' 'b' 'c' as abc %}{% resetcycle undefinedcycle %}", {}, template.TemplateSyntaxError),
     827            'resetcycle06': ("{% for i in test %}{% cycle 'a' 'b' %}{% resetcycle %}{% endfor %}", {'test': range(5)}, 'aaaaa'),
     828            'resetcycle07': ("{% cycle 'a' 'b' 'c' as abc %}{% for i in test %}{% cycle abc %}{% cycle '-' '+' %}{% resetcycle %}{% endfor %}", {'test': range(5)}, 'ab-c-a-b-c-'),
     829            '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-'),
     830            'resetcycle09': ("{% for i in outer %}{% for j in inner %}{% cycle 'a' 'b' %}{% endfor %}{% resetcycle %}{% endfor %}", {'outer': range(2), 'inner': range(3)}, 'abaaba'),
     831            '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'),
     832            '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'),
     833            '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'),
     834
    822835            ### EXCEPTIONS ############################################################
    823836
    824837            # Raise exception for invalid template name
Back to Top