From 002fe076225c2aa6e389481b038f55acb6f807d0 Mon Sep 17 00:00:00 2001
From: kapil garg <kapilgarg1996@users.noreply.github.com>
Date: Tue, 4 Apr 2017 07:59:39 +0530
Subject: [PATCH] Fixed #27974 -- Kept resolved templates constant during one
 rendering cycle.

Thanks Florian Apolloner for the initial patch.
---
 django/template/loader_tags.py                |  2 +-
 .../syntax_tests/test_if_changed.py           | 11 ++++++++++
 .../syntax_tests/test_include.py              | 20 +++++++++++++++++++
 tests/template_tests/templatetags/custom.py   | 15 ++++++++++++++
 4 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/django/template/loader_tags.py b/django/template/loader_tags.py
index 0f70a39655..69e7ce471d 100644
--- a/django/template/loader_tags.py
+++ b/django/template/loader_tags.py
@@ -176,7 +176,7 @@ class IncludeNode(Node):
             if not callable(getattr(template, 'render', None)):
                 # If not, we'll try our cache, and get_template()
                 template_name = template
-                cache = context.render_context.setdefault(self.context_key, {})
+                cache = context.render_context.dicts[0].setdefault(self, {})
                 template = cache.get(template_name)
                 if template is None:
                     template = context.template.engine.get_template(template_name)
diff --git a/tests/template_tests/syntax_tests/test_if_changed.py b/tests/template_tests/syntax_tests/test_if_changed.py
index 889a24fcd8..2e6b2c9684 100644
--- a/tests/template_tests/syntax_tests/test_if_changed.py
+++ b/tests/template_tests/syntax_tests/test_if_changed.py
@@ -212,3 +212,14 @@ class IfChangedTests(SimpleTestCase):
         ])
         output = engine.render_to_string('template', dict(vars=[1, 1, 2, 2, 3, 3]))
         self.assertEqual(output, "123")
+
+    def test_include_state(self):
+        """Tests the node state for different IncludeNodes (#27974)."""
+        engine = Engine(loaders=[
+            ('django.template.loaders.locmem.Loader', {
+                'template': '{% for x in vars %}{% include "include" %}{% include "include" %}{% endfor %}',
+                'include': '{% ifchanged %}{{ x }}{% endifchanged %}',
+            }),
+        ])
+        output = engine.render_to_string('template', dict(vars=[1, 1, 2, 2, 3, 3]))
+        self.assertEqual(output, '112233')
diff --git a/tests/template_tests/syntax_tests/test_include.py b/tests/template_tests/syntax_tests/test_include.py
index 904209ea8b..5f92efc05a 100644
--- a/tests/template_tests/syntax_tests/test_include.py
+++ b/tests/template_tests/syntax_tests/test_include.py
@@ -308,3 +308,23 @@ class IncludeTests(SimpleTestCase):
             "Recursion!  A1  Recursion!  B1   B2   B3  Recursion!  C1",
             t.render(Context({'comments': comments})).replace(' ', '').replace('\n', ' ').strip(),
         )
+
+    def test_include_cache(self):
+        """
+        {% include %} keeps resolved templates constant (#27974). The
+        CounterNode object in the {% counter %} template tag is created once
+        if caching works properly. Each iteration increases the counter instead
+        of restarting it.
+
+        This works as a regression test only if the cached loader
+        isn't used, so the @setup decorator isn't used.
+        """
+        engine = Engine(loaders=[
+            ('django.template.loaders.locmem.Loader', {
+                'template': '{% for x in vars %}{% include "include" %}{% endfor %}',
+                'include': '{% include "next" %}',
+                'next': '{% load custom %}{% counter %}'
+            }),
+        ], libraries={'custom': 'template_tests.templatetags.custom'})
+        output = engine.render_to_string('template', dict(vars=range(9)))
+        self.assertEqual(output, '012345678')
diff --git a/tests/template_tests/templatetags/custom.py b/tests/template_tests/templatetags/custom.py
index 485f49a82d..7c40bc9cea 100644
--- a/tests/template_tests/templatetags/custom.py
+++ b/tests/template_tests/templatetags/custom.py
@@ -166,3 +166,18 @@ def minustwo_overridden_name(value):
 
 
 register.simple_tag(lambda x: x - 1, name='minusone')
+
+
+@register.tag('counter')
+def counter(parser, token):
+    return CounterNode()
+
+
+class CounterNode(template.Node):
+    def __init__(self):
+        self.count = 0
+
+    def render(self, context):
+        count = self.count
+        self.count = count + 1
+        return count