From 35bbb2c9c01882b1d77b0b8c737ac646144833d4 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Fri, 29 Sep 2023 19:02:19 +0200 Subject: [PATCH] Fixed #34883 -- Allowed template tags to set extra data on templates. By setting a value in the `parser.extra_data` mapping, template tags pass additional data out of the parsing context. Any extra data set is exposed on the template via the matching `.extra_data` attribute. Library authors should use a key to namespace extra data. The 'django' namespace is reserved for internal use. --- django/template/base.py | 10 +++++++++- docs/releases/5.1.txt | 4 +++- tests/template_tests/templatetags/custom.py | 7 +++++++ tests/template_tests/tests.py | 9 +++++++++ 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/django/template/base.py b/django/template/base.py index f7e4f94f51..0f1eca58db 100644 --- a/django/template/base.py +++ b/django/template/base.py @@ -193,7 +193,9 @@ class Template: ) try: - return parser.parse() + nodelist = parser.parse() + self.extra_data = parser.extra_data + return nodelist except Exception as e: if self.engine.debug: e.template_debug = self.get_exception_info(e, e.token) @@ -439,6 +441,12 @@ class Parser: self.filters = {} self.command_stack = [] + # Custom template tags may store additional data on the parser that + # will be made available on the template instance. Library authors + # should use a key to namespace any added data. The 'django' namespace + # is reserved for internal use. + self.extra_data = {} + if libraries is None: libraries = {} if builtins is None: diff --git a/docs/releases/5.1.txt b/docs/releases/5.1.txt index 1e2eb97557..bd70a2c7ac 100644 --- a/docs/releases/5.1.txt +++ b/docs/releases/5.1.txt @@ -194,7 +194,9 @@ Signals Templates ~~~~~~~~~ -* ... +* Custom tags may now set extra data on the ``Parser`` object that will later + be made available on the ``Template`` instance. Such data may be used, for + example, by the template loader, or other template clients. Tests ~~~~~ diff --git a/tests/template_tests/templatetags/custom.py b/tests/template_tests/templatetags/custom.py index 0937085c8c..8d1130ae78 100644 --- a/tests/template_tests/templatetags/custom.py +++ b/tests/template_tests/templatetags/custom.py @@ -1,4 +1,5 @@ from django import template +from django.template.base import TextNode from django.template.defaultfilters import stringfilter from django.utils.html import escape, format_html from django.utils.safestring import mark_safe @@ -216,3 +217,9 @@ class CounterNode(template.Node): count = self.count self.count = count + 1 return str(count) + + +@register.tag("extra_data") +def do_extra_data(parser, token): + parser.extra_data["extra_data"] = "CUSTOM_DATA" + return TextNode("") diff --git a/tests/template_tests/tests.py b/tests/template_tests/tests.py index d9bdd6e68f..14df81669b 100644 --- a/tests/template_tests/tests.py +++ b/tests/template_tests/tests.py @@ -140,6 +140,15 @@ class TemplateTestMixin: if self.debug_engine: self.assertEqual(e.exception.template_debug["during"], "{% badtag %}") + def test_compile_tag_extra_data(self): + """Custom tags can pass extra data back to template.""" + engine = self._engine( + app_dirs=True, + libraries={"custom": "template_tests.templatetags.custom"}, + ) + t = engine.from_string("{% load custom %}{% extra_data %}") + self.assertEqual(t.extra_data["extra_data"], "CUSTOM_DATA") + def test_render_tag_error_in_extended_block(self): """Errors in extended block are displayed correctly.""" e = self._engine(app_dirs=True)