Fixed #13567 -- Added a 'silent' argument to the cycle tag, so that you can declare a cycle without producing a value in the template. Thanks to anentropic for the suggestion and initial patch, and Łukasz Rekucki for the final patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14439 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Russell Keith-Magee 2010-11-02 13:42:30 +00:00
parent bb062c376f
commit a89e02637b
3 changed files with 55 additions and 3 deletions

View File

@ -53,13 +53,17 @@ class CsrfTokenNode(Node):
return u'' return u''
class CycleNode(Node): class CycleNode(Node):
def __init__(self, cyclevars, variable_name=None): def __init__(self, cyclevars, variable_name=None, silent=False):
self.cyclevars = cyclevars self.cyclevars = cyclevars
self.variable_name = variable_name self.variable_name = variable_name
self.silent = silent
def render(self, context): def render(self, context):
if self not in context.render_context: if self not in context.render_context:
# First time the node is rendered in template
context.render_context[self] = itertools_cycle(self.cyclevars) context.render_context[self] = itertools_cycle(self.cyclevars)
if self.silent:
return ''
cycle_iter = context.render_context[self] cycle_iter = context.render_context[self]
value = cycle_iter.next().resolve(context) value = cycle_iter.next().resolve(context)
if self.variable_name: 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 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 be used to separate values; if a comma is used, the cycle values are
interpreted as literal strings. 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 %}
<tr class="{% cycle rowcolors %}">{# first value will be "row1" #}
...
</tr>
{% endfor %}
""" """
# Note: This returns the exact same node on each {% cycle name %} call; # 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) raise TemplateSyntaxError("Named cycle '%s' does not exist" % name)
return parser._namedCycleNodes[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] name = args[-1]
values = [parser.compile_filter(arg) for arg in args[1:-2]] 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'): if not hasattr(parser, '_namedCycleNodes'):
parser._namedCycleNodes = {} parser._namedCycleNodes = {}
parser._namedCycleNodes[name] = node parser._namedCycleNodes[name] = node

View File

@ -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 way to specify variable values. Or literal commas. Or spaces. Did we mention
you shouldn't use this syntax in any new projects? 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 %}
<tr class="{% cycle rowcolors %}">{{ obj }}</tr>
{% endfor %}
This will output a list of ``<tr>`` 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 ``<tr>`` elements, and the first ``<tr>`` would have a
class of ``row2``.
.. templatetag:: debug .. templatetag:: debug
debug debug

View File

@ -646,6 +646,9 @@ class Templates(unittest.TestCase):
'cycle14': ("{% cycle one two as foo %}{% cycle foo %}", {'one': '1','two': '2'}, '12'), '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,'), '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'), '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 ############################################################ ### EXCEPTIONS ############################################################