Add changed() to loop context

This commit is contained in:
Adrian Moennich 2017-02-01 21:47:17 +01:00
parent 9bdb547b0e
commit bc076b3e25
5 changed files with 38 additions and 0 deletions

View File

@ -13,6 +13,9 @@ Version 2.10
- Added `previtem` and `nextitem` to loop contexts, providing access to the
previous/next item in the loop. If such an item does not exist, the value is
undefined.
- Added `changed(*values)` to loop contexts, providing an easy way of checking
whether a value has changed since the last iteration (or rather since the
last call of the method)
Version 2.9.6
-------------

View File

@ -618,6 +618,9 @@ Inside of a for-loop block, you can access some special variables:
| `loop.nextitem` | The item from the following iteration of the loop.|
| | Undefined during the last iteration. |
+-----------------------+---------------------------------------------------+
| `loop.changed(*val)` | True if previously called with a different value |
| | (or not called at all). |
+-----------------------+---------------------------------------------------+
Within a for-loop, it's possible to cycle among a list of strings/variables
each time through the loop by using the special `loop.cycle` helper::
@ -700,6 +703,16 @@ and `nextitem`::
{% endif %}
{% endfor %}
If you only care whether the value changed at all, using `changed` is even
easier::
{% for entry in entries %}
{% if loop.changed(entry.category) %}
<h2>{{ entry.category }}</h2>
{% endif %}
<p>{{ entry.message }}</p>
{% endfor %}
.. _if:
If

View File

@ -360,6 +360,7 @@ class LoopContextBase(object):
self._recurse = recurse
self.index0 = -1
self.depth0 = depth0
self._last_checked_value = missing
def cycle(self, *args):
"""Cycles among the arguments with the current loop index."""
@ -367,6 +368,13 @@ class LoopContextBase(object):
raise TypeError('no items for cycling given')
return args[self.index0 % len(args)]
def changed(self, *value):
"""Checks whether the value has changed since the last call."""
if self._last_checked_value != value:
self._last_checked_value = value
return True
return False
first = property(lambda x: x.index0 == 0)
last = property(lambda x: x._after is _last_iteration)
index = property(lambda x: x.index0 + 1)

View File

@ -312,6 +312,13 @@ class TestAsyncForLoop(object):
output = tmpl.render(seq=list(range(4)))
assert output == 'x-0-1|0-1-2|1-2-3|2-3-x|'
def test_changed(self, test_env_async):
tmpl = test_env_async.from_string('''{% for item in seq -%}
{{ loop.changed(item) }},
{%- endfor %}''')
output = tmpl.render(seq=[None, None, 1, 2, 2, 3, 4, 4, 4])
assert output == 'True,False,True,True,False,True,True,False,False,'
def test_scope(self, test_env_async):
tmpl = test_env_async.from_string('{% for item in seq %}{% endfor %}{{ item }}')
output = tmpl.render(seq=list(range(10)))

View File

@ -76,6 +76,13 @@ class TestForLoop(object):
output = tmpl.render(seq=list(range(4)))
assert output == 'x-0-1|0-1-2|1-2-3|2-3-x|'
def test_changed(self, env):
tmpl = env.from_string('''{% for item in seq -%}
{{ loop.changed(item) }},
{%- endfor %}''')
output = tmpl.render(seq=[None, None, 1, 2, 2, 3, 4, 4, 4])
assert output == 'True,False,True,True,False,True,True,False,False,'
def test_scope(self, env):
tmpl = env.from_string('{% for item in seq %}{% endfor %}{{ item }}')
output = tmpl.render(seq=list(range(10)))