summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Schrijver <alex@flupzor.nl>2016-07-16 22:55:40 +0200
committerTim Graham <timograham@gmail.com>2016-08-09 09:05:17 -0400
commit3ae3a1f9fad800d73892b14ac8a576e817a2da55 (patch)
tree3ddbfc34b1c8f9dd76702b581f2364c4e7e85a71
parent31e053edfa01e47c26dfa03834beeba77e09403a (diff)
Fixed #26830 -- Prevented the 'with' templatetag from resetting the cycle variable to its initial state.
-rw-r--r--django/template/context.py12
-rw-r--r--django/template/defaulttags.py2
-rw-r--r--tests/template_tests/syntax_tests/test_cycle.py22
-rw-r--r--tests/template_tests/test_context.py31
4 files changed, 66 insertions, 1 deletions
diff --git a/django/template/context.py b/django/template/context.py
index 361f3503ca..1e1c391229 100644
--- a/django/template/context.py
+++ b/django/template/context.py
@@ -67,6 +67,18 @@ class BaseContext(object):
"Set a variable in the current context"
self.dicts[-1][key] = value
+ def set_upward(self, key, value):
+ """
+ Set a variable in one of the higher contexts if it exists there,
+ otherwise in the current context.
+ """
+ context = self.dicts[-1]
+ for d in reversed(self.dicts):
+ if key in d.keys():
+ context = d
+ break
+ context[key] = value
+
def __getitem__(self, key):
"Get a variable's value, starting at the current context and going upward"
for d in reversed(self.dicts):
diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
index bfe00ca83d..87ac5e2d83 100644
--- a/django/template/defaulttags.py
+++ b/django/template/defaulttags.py
@@ -82,7 +82,7 @@ class CycleNode(Node):
cycle_iter = context.render_context[self]
value = next(cycle_iter).resolve(context)
if self.variable_name:
- context[self.variable_name] = value
+ context.set_upward(self.variable_name, value)
if self.silent:
return ''
return render_value_in_context(value, context)
diff --git a/tests/template_tests/syntax_tests/test_cycle.py b/tests/template_tests/syntax_tests/test_cycle.py
index e98a7ec509..b5712b54bb 100644
--- a/tests/template_tests/syntax_tests/test_cycle.py
+++ b/tests/template_tests/syntax_tests/test_cycle.py
@@ -145,3 +145,25 @@ class CycleTagTests(SimpleTestCase):
"""
output = self.engine.render_to_string('cycle29', {'values': [1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 9, 9]})
self.assertEqual(output, 'bcabcabcccaa')
+
+ @setup({
+ 'cycle30': "{% cycle 'a' 'b' 'c' as cycler silent %}"
+ "{% for x in values %}"
+ "{% with doesnothing=irrelevant %}"
+ "{% ifchanged x %}"
+ "{% cycle cycler %}{{ cycler }}"
+ "{% else %}"
+ "{{ cycler }}"
+ "{% endifchanged %}"
+ "{% endwith %}"
+ "{% endfor %}"})
+ def test_cycle30(self):
+ """
+ A {% with %} tag shouldn't reset the {% cycle %} variable.
+ """
+ output = self.engine.render_to_string(
+ 'cycle30', {
+ 'irrelevant': 1,
+ 'values': [1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 9, 9]
+ })
+ self.assertEqual(output, 'bcabcabcccaa')
diff --git a/tests/template_tests/test_context.py b/tests/template_tests/test_context.py
index 94011fb0f1..c061610a19 100644
--- a/tests/template_tests/test_context.py
+++ b/tests/template_tests/test_context.py
@@ -204,6 +204,37 @@ class ContextTests(SimpleTestCase):
self.assertEqual(str(warns[2].message), msg2)
self.assertEqual(str(warns[3].message), msg2)
+ def test_set_upward(self):
+ c = Context({'a': 1})
+ c.set_upward('a', 2)
+ self.assertEqual(c.get('a'), 2)
+
+ def test_set_upward_empty_context(self):
+ empty_context = Context()
+ empty_context.set_upward('a', 1)
+ self.assertEqual(empty_context.get('a'), 1)
+
+ def test_set_upward_with_push(self):
+ """
+ The highest context which has the given key is used.
+ """
+ c = Context({'a': 1})
+ c.push({'a': 2})
+ c.set_upward('a', 3)
+ self.assertEqual(c.get('a'), 3)
+ c.pop()
+ self.assertEqual(c.get('a'), 1)
+
+ def test_set_upward_with_push_no_match(self):
+ """
+ The highest context is used if the given key isn't found.
+ """
+ c = Context({'b': 1})
+ c.push({'b': 2})
+ c.set_upward('a', 2)
+ self.assertEqual(len(c.dicts), 3)
+ self.assertEqual(c.dicts[-1]['a'], 2)
+
class RequestContextTests(SimpleTestCase):