From: Adrian Moennich Date: Wed, 1 Feb 2017 20:47:17 +0000 (+0100) Subject: Add changed() to loop context X-Git-Tag: 2.10~25^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bc076b3e25fa43d5be027d5ec87ebede85738c46;p=thirdparty%2Fjinja.git Add changed() to loop context --- diff --git a/CHANGES b/CHANGES index afb26c18..a684130b 100644 --- a/CHANGES +++ b/CHANGES @@ -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 ------------- diff --git a/docs/templates.rst b/docs/templates.rst index f259f839..5defc8bb 100644 --- a/docs/templates.rst +++ b/docs/templates.rst @@ -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) %} +

{{ entry.category }}

+ {% endif %} +

{{ entry.message }}

+ {% endfor %} + .. _if: If diff --git a/jinja2/runtime.py b/jinja2/runtime.py index 2f91a8c8..a75ae6ef 100644 --- a/jinja2/runtime.py +++ b/jinja2/runtime.py @@ -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) diff --git a/tests/test_async.py b/tests/test_async.py index 83eb9376..2f177473 100644 --- a/tests/test_async.py +++ b/tests/test_async.py @@ -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))) diff --git a/tests/test_core_tags.py b/tests/test_core_tags.py index 0fb4be8f..4f56db78 100644 --- a/tests/test_core_tags.py +++ b/tests/test_core_tags.py @@ -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)))