浏览代码

Fixed #35493 -- Allowed template self-inclusion with relative paths.

Co-authored-by: Brock <bsmick97@gmail.com>
Gabriel Nick Pivovarov 3 月之前
父节点
当前提交
55855bc6d0

+ 11 - 3
django/template/loader_tags.py

@@ -242,7 +242,11 @@ def do_block(parser, token):
     return BlockNode(block_name, nodelist)
 
 
-def construct_relative_path(current_template_name, relative_name):
+def construct_relative_path(
+    current_template_name,
+    relative_name,
+    allow_recursion=False,
+):
     """
     Convert a relative path (starting with './' or '../') to the full template
     name based on the current_template_name.
@@ -264,7 +268,7 @@ def construct_relative_path(current_template_name, relative_name):
             "The relative path '%s' points outside the file hierarchy that "
             "template '%s' is in." % (relative_name, current_template_name)
         )
-    if current_template_name.lstrip("/") == new_name:
+    if not allow_recursion and current_template_name.lstrip("/") == new_name:
         raise TemplateSyntaxError(
             "The relative path '%s' was translated to template name '%s', the "
             "same template in which the tag appears."
@@ -346,7 +350,11 @@ def do_include(parser, token):
         options[option] = value
     isolated_context = options.get("only", False)
     namemap = options.get("with", {})
-    bits[1] = construct_relative_path(parser.origin.template_name, bits[1])
+    bits[1] = construct_relative_path(
+        parser.origin.template_name,
+        bits[1],
+        allow_recursion=True,
+    )
     return IncludeNode(
         parser.compile_filter(bits[1]),
         extra_context=namemap,

+ 37 - 9
tests/template_tests/syntax_tests/test_include.py

@@ -330,15 +330,43 @@ class IncludeTests(SimpleTestCase):
                 ],
             }
         ]
-        engine = Engine(app_dirs=True)
-        t = engine.get_template("recursive_include.html")
-        self.assertEqual(
-            "Recursion!  A1  Recursion!  B1   B2   B3  Recursion!  C1",
-            t.render(Context({"comments": comments}))
-            .replace(" ", "")
-            .replace("\n", " ")
-            .strip(),
-        )
+        with self.subTest(template="recursive_include.html"):
+            engine = Engine(app_dirs=True)
+            t = engine.get_template("recursive_include.html")
+            self.assertEqual(
+                "Recursion!  A1  Recursion!  B1   B2   B3  Recursion!  C1",
+                t.render(Context({"comments": comments}))
+                .replace(" ", "")
+                .replace("\n", " ")
+                .strip(),
+            )
+        with self.subTest(template="recursive_relative_include.html"):
+            engine = Engine(app_dirs=True)
+            t = engine.get_template("recursive_relative_include.html")
+            self.assertEqual(
+                "Recursion!  A1  Recursion!  B1   B2   B3  Recursion!  C1",
+                t.render(Context({"comments": comments}))
+                .replace(" ", "")
+                .replace("\n", " ")
+                .strip(),
+            )
+        with self.subTest(template="tmpl"):
+            engine = Engine()
+            template = """
+            Recursion!
+            {% for c in comments %}
+              {{ c.comment }}
+              {% if c.children %}{% include tmpl with comments=c.children %}{% endif %}
+            {% endfor %}
+            """
+            outer_tmpl = engine.from_string("{% include tmpl %}")
+            output = outer_tmpl.render(
+                Context({"tmpl": engine.from_string(template), "comments": comments})
+            )
+            self.assertEqual(
+                "Recursion!  A1  Recursion!  B1   B2   B3  Recursion!  C1",
+                output.replace(" ", "").replace("\n", " ").strip(),
+            )
 
     def test_include_cache(self):
         """

+ 7 - 0
tests/template_tests/templates/recursive_relative_include.html

@@ -0,0 +1,7 @@
+Recursion!
+{% for comment in comments %}
+    {{ comment.comment }}
+    {% if comment.children %}
+        {% include "./recursive_relative_include.html" with comments=comment.children %}
+    {% endif %}
+{% endfor %}