Django cycle tag that doesn't continue where it left off

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
from django import template
from django.template import Node, Variable
from django.template.base import TemplateSyntaxError
from itertools import cycle as itertools_cycle

"""{% safe_cycle %}

This tag is equivalent to {% cycle %} but resets when we exit the
containing loop.

See Django ticket "Cycle tag should reset after it steps out of scope"
https://code.djangoproject.com/ticket/5908

This code is a lightly modified version of Simon Litchfield's
attachment on that ticket.

"""

register = template.Library()

   
class SafeCycleNode(Node):
    def __init__(self, cyclevars, variable_name=None):
        self.cyclevars = cyclevars
        self.cycle_iter = itertools_cycle(cyclevars)
        self.variable_name = variable_name

    def render(self, context):
        if context.has_key('forloop'):
            if not context.get(self):
                context[self] = True
                self.cycle_iter = itertools_cycle(self.cyclevars)
        value = self.cycle_iter.next()
        value = Variable(value).resolve(context)
        if self.variable_name:
            context[self.variable_name] = value
        return value


@register.tag
def safe_cycle(parser, token):
    """
    Cycles among the given strings each time this tag is encountered.

    Within a loop, cycles among the given strings each time through
    the loop::

        {% for o in some_list %}
            <tr class="{% cycle 'row1' 'row2' %}">
                ...
            </tr>
        {% endfor %}

    Outside of a loop, give the values a unique name the first time you call
    it, then use that name each sucessive time through::

            <tr class="{% cycle 'row1' 'row2' 'row3' as rowcolors %}">...</tr>
            <tr class="{% cycle rowcolors %}">...</tr>
            <tr class="{% cycle rowcolors %}">...</tr>

    You can use any number of values, seperated by spaces. Commas can also
    be used to separate values; if a comma is used, the cycle values are
    interpreted as literal strings.
    """

    # Note: This returns the exact same node on each {% cycle name %} call;
    # that is, the node object returned from {% cycle a b c as name %} and the
    # one returned from {% cycle name %} are the exact same object.  This
    # shouldn't cause problems (heh), but if it does, now you know.
    #
    # Ugly hack warning: this stuffs the named template dict into parser so
    # that names are only unique within each template (as opposed to using
    # a global variable, which would make cycle names have to be unique across
    # *all* templates.

    args = token.split_contents()

    if len(args) < 2:
        raise TemplateSyntaxError("'cycle' tag requires at least two arguments")

    if ',' in args[1]:
        # Backwards compatibility: {% cycle a,b %} or {% cycle a,b as foo %}
        # case.
        args[1:2] = ['"%s"' % arg for arg in args[1].split(",")]

    if len(args) == 2:
        # {% cycle foo %} case.
        name = args[1]
        if not hasattr(parser, '_namedCycleNodes'):
            raise TemplateSyntaxError("No named cycles in template."
                                      " '%s' is not defined" % name)
        if not name in parser._namedCycleNodes:
            raise TemplateSyntaxError("Named cycle '%s' does not exist" % name)
        return parser._namedCycleNodes[name]

    if len(args) > 4 and args[-2] == 'as':
        name = args[-1]
        node = SafeCycleNode(args[1:-2], name)
        if not hasattr(parser, '_namedCycleNodes'):
            parser._namedCycleNodes = {}
        parser._namedCycleNodes[name] = node
    else:
        node = SafeCycleNode(args[1:])
    return node

More like this

  1. Work around issue 10827 by scjody 1 year, 11 months ago
  2. Set Template Tag by Xin 6 years, 11 months ago
  3. reset django password by hugogee 2 years, 8 months ago
  4. Hidden Date Display Widget for Admin by andrew.schoen 4 years, 9 months ago
  5. extend tag with cache by fredd4 6 years, 2 months ago

Comments

(Forgotten your password?)