diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index 1b07413530..687a74477f 100644 --- a/django/template/defaulttags.py +++ b/django/template/defaulttags.py @@ -53,13 +53,17 @@ class CsrfTokenNode(Node): return u'' class CycleNode(Node): - def __init__(self, cyclevars, variable_name=None): + def __init__(self, cyclevars, variable_name=None, silent=False): self.cyclevars = cyclevars self.variable_name = variable_name + self.silent = silent def render(self, context): if self not in context.render_context: + # First time the node is rendered in template context.render_context[self] = itertools_cycle(self.cyclevars) + if self.silent: + return '' cycle_iter = context.render_context[self] value = cycle_iter.next().resolve(context) if self.variable_name: @@ -482,6 +486,17 @@ def cycle(parser, token): You can use any number of values, separated by spaces. Commas can also be used to separate values; if a comma is used, the cycle values are interpreted as literal strings. + + The optional flag "silent" can be used to prevent the cycle declaration + from returning any value:: + + {% cycle 'row1' 'row2' as rowcolors silent %}{# no value here #} + {% for o in some_list %} + {# first value will be "row1" #} + ... + + {% endfor %} + """ # Note: This returns the exact same node on each {% cycle name %} call; @@ -513,10 +528,24 @@ def cycle(parser, token): raise TemplateSyntaxError("Named cycle '%s' does not exist" % name) return parser._namedCycleNodes[name] - if len(args) > 4 and args[-2] == 'as': + as_form = False + + if len(args) > 4: + # {% cycle ... as foo [silent] %} case. + if args[-3] == "as": + if args[-1] != "silent": + raise TemplateSyntaxError("Only 'silent' flag is allowed after cycle's name, not '%s'." % args[-1]) + as_form = True + silent = True + args = args[:-1] + elif args[-2] == "as": + as_form = True + silent = False + + if as_form: name = args[-1] values = [parser.compile_filter(arg) for arg in args[1:-2]] - node = CycleNode(values, name) + node = CycleNode(values, name, silent=silent) if not hasattr(parser, '_namedCycleNodes'): parser._namedCycleNodes = {} parser._namedCycleNodes[name] = node diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt index 7bb4481904..5fdef71a5f 100644 --- a/docs/ref/templates/builtins.txt +++ b/docs/ref/templates/builtins.txt @@ -140,6 +140,26 @@ In this syntax, each value gets interpreted as a literal string, and there's no way to specify variable values. Or literal commas. Or spaces. Did we mention you shouldn't use this syntax in any new projects? +.. versionadded:: 1.3 + +By default, when you use the ``as`` keyword with the cycle tag, the +usage of ``{% cycle %}`` that declares the cycle will itself output +the first value in the cycle. This could be a problem if you want to +use the value in a nested loop or an included template. If you want to +just declare the cycle, but not output the first value, you can add a +``silent`` keyword as the last keyword in the tag. For example:: + + {% cycle 'row1' 'row2' as rowcolors silent %} + {% for obj in some_list %} + {{ obj }} + {% endfor %} + +This will output a list of ```` elements with ``class`` +alternating between ``row1`` and ``row2``. If the ``silent`` keyword +were to be omitted, ``row1`` would be emitted as normal text, outside +the list of ```` elements, and the first ```` would have a +class of ``row2``. + .. templatetag:: debug debug diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py index 492d795117..2da6d48de7 100644 --- a/tests/regressiontests/templates/tests.py +++ b/tests/regressiontests/templates/tests.py @@ -646,6 +646,9 @@ class Templates(unittest.TestCase): 'cycle14': ("{% cycle one two as foo %}{% cycle foo %}", {'one': '1','two': '2'}, '12'), 'cycle15': ("{% for i in test %}{% cycle aye bee %}{{ i }},{% endfor %}", {'test': range(5), 'aye': 'a', 'bee': 'b'}, 'a0,b1,a2,b3,a4,'), 'cycle16': ("{% cycle one|lower two as foo %}{% cycle foo %}", {'one': 'A','two': '2'}, 'a2'), + 'cycle17': ("{% cycle 'a' 'b' 'c' as abc silent %}{% cycle abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}", {}, "abca"), + 'cycle18': ("{% cycle 'a' 'b' 'c' as foo invalid_flag %}", {}, template.TemplateSyntaxError), + 'cycle19': ("{% cycle 'a' 'b' as silent %}{% cycle silent %}", {}, "ab"), ### EXCEPTIONS ############################################################