summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/template/base.py19
-rw-r--r--docs/internals/deprecation.txt3
-rw-r--r--docs/releases/6.1.txt4
-rw-r--r--tests/template_tests/syntax_tests/test_basic.py49
4 files changed, 73 insertions, 2 deletions
diff --git a/django/template/base.py b/django/template/base.py
index 9d75111e42..8c6390de33 100644
--- a/django/template/base.py
+++ b/django/template/base.py
@@ -57,7 +57,7 @@ import warnings
from enum import Enum
from django.template.context import BaseContext
-from django.utils.deprecation import django_file_prefixes
+from django.utils.deprecation import RemovedInDjango70Warning, django_file_prefixes
from django.utils.formats import localize
from django.utils.html import conditional_escape
from django.utils.inspect import lazy_annotations, signature
@@ -555,6 +555,23 @@ class Parser:
except TemplateSyntaxError as e:
raise self.error(token, e)
var_node = VariableNode(filter_expression)
+ if ".." in str(filter_expression.var):
+ warnings.warn(
+ "Support for double-dot lookups '..' which maps to a "
+ "lookup of the empty string is deprecated.\n"
+ f" Template: {self.origin.name}\n"
+ f" Line: {token.lineno}",
+ RemovedInDjango70Warning,
+ skip_file_prefixes=django_file_prefixes(),
+ )
+
+ # RemovedInDjango70Warning
+ # When deprecation ends elevate the warning to an error.
+ # raise self.error(
+ # token,
+ # ("Variable contains '..' on line %d" % token.lineno),
+ # )
+
self.extend_nodelist(nodelist, var_node, token)
elif token_type == 2: # TokenType.BLOCK
try:
diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt
index c8e467a0ee..3cda71933b 100644
--- a/docs/internals/deprecation.txt
+++ b/docs/internals/deprecation.txt
@@ -77,6 +77,9 @@ details on these changes.
``django.contrib.postgres.aggregates.BitOr``, and
``django.contrib.postgres.aggregates.BitXor`` classes will be removed.
+* Support for double-dot variable lookup, like ``{{ book..title }}``,
+ is removed.
+
.. _deprecation-removed-in-6.1:
6.1
diff --git a/docs/releases/6.1.txt b/docs/releases/6.1.txt
index 48f8939041..3ec680702c 100644
--- a/docs/releases/6.1.txt
+++ b/docs/releases/6.1.txt
@@ -573,6 +573,10 @@ Miscellaneous
:class:`~django.db.models.BitOr`, and :class:`~django.db.models.BitXor`
classes.
+* Support for a double-dot variable lookup like ``{{ book..title }}`` which
+ maps to a lookup of the empty string before the next lookup of the named
+ attribute is deprecated.
+
Features removed in 6.1
=======================
diff --git a/tests/template_tests/syntax_tests/test_basic.py b/tests/template_tests/syntax_tests/test_basic.py
index 04cf5f4401..47bc949fdc 100644
--- a/tests/template_tests/syntax_tests/test_basic.py
+++ b/tests/template_tests/syntax_tests/test_basic.py
@@ -1,7 +1,9 @@
+from django.template import Engine
from django.template.base import Origin, Template, TemplateSyntaxError
from django.template.context import Context
from django.template.loader_tags import BlockContext, BlockNode
-from django.test import SimpleTestCase
+from django.test import SimpleTestCase, ignore_warnings
+from django.utils.deprecation import RemovedInDjango70Warning
from django.views.debug import ExceptionReporter
from ..utils import SilentAttrClass, SilentGetItemClass, SomeClass, setup
@@ -393,6 +395,42 @@ class BasicSyntaxTests(SimpleTestCase):
output = self.engine.render_to_string("template", {"meals": Meals})
self.assertEqual(output, "soup is yummy.")
+ def test_double_dot_lookup(self):
+ loaders = [
+ (
+ "django.template.loaders.cached.Loader",
+ [
+ (
+ "django.template.loaders.locmem.Loader",
+ {"template": "{{ doubledot..lookup }}"},
+ ),
+ ],
+ ),
+ ]
+
+ msg = (
+ "Support for double-dot lookups '..' which maps to a lookup of the empty "
+ "string is deprecated.\n Template: template\n Line: 1"
+ )
+
+ for debug in [True, False]:
+ with self.subTest(debug=debug):
+ engine = Engine(loaders=loaders, debug=debug)
+ with self.assertWarnsMessage(RemovedInDjango70Warning, msg):
+ engine.render_to_string("template", {})
+ # Cached loader results in warning only on first access.
+ engine.render_to_string("template", {})
+
+ # RemovedInDjango70Warning.
+ # Replace the above test with the following.
+ # @setup({"template": "{{ doubledot..lookup }}"})
+ # def test_double_dot_lookup(self):
+ # with self.assertRaisesMessage(
+ # TemplateSyntaxError,
+ # "Variable contains '..' on line 1",
+ # ):
+ # self.engine.render_to_string("template")
+
class BlockContextTests(SimpleTestCase):
def test_repr(self):
@@ -429,3 +467,12 @@ class TemplateNameInExceptionTests(SimpleTestCase):
Template("{% endfor %}")
except TemplateSyntaxError as e:
self.assertEqual(str(e), self.template_error_msg)
+
+
+# RemovedInDjango70Warning
+@ignore_warnings(category=RemovedInDjango70Warning)
+class DeprecatedTests(SimpleTestCase):
+ @setup({"template": "{{ doubledot..lookup }}"})
+ def test_double_dot_lookup(self):
+ context = Context({"doubledot": {"": {"lookup": "value"}}})
+ self.assertEqual(self.engine.render_to_string("template", context), "value")