diff --git a/django/template/loader_tags.py b/django/template/loader_tags.py index 8fa3a14087..b96214cad1 100644 --- a/django/template/loader_tags.py +++ b/django/template/loader_tags.py @@ -168,12 +168,16 @@ class IncludeNode(Node): template = self.template.resolve(context) # Does this quack like a Template? if not callable(getattr(template, 'render', None)): - # If not, try the cache and get_template(). - template_name = template + # If not, try the cache and select_template(). + template_name = template or () + if isinstance(template_name, str): + template_name = (template_name,) + else: + template_name = tuple(template_name) 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) + template = context.template.engine.select_template(template_name) cache[template_name] = template # Use the base.Template of a backends.django.Template. elif hasattr(template, 'template'): diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt index 4d65dc82f7..7172bfc3f2 100644 --- a/docs/ref/templates/builtins.txt +++ b/docs/ref/templates/builtins.txt @@ -688,6 +688,10 @@ the variable ``template_name``:: The variable may also be any object with a ``render()`` method that accepts a context. This allows you to reference a compiled ``Template`` in your context. +Additionally, the variable may be an iterable of template names, in which case +the first that can be loaded will be used, as per +:func:`~django.template.loader.select_template`. + An included template is rendered within the context of the template that includes it. This example produces the output ``"Hello, John!"``: @@ -724,6 +728,10 @@ available to the included template:: been evaluated and rendered* - not blocks that can be overridden by, for example, an extending template. +.. versionchanged:: 3.1 + + Support for iterables of template names was added. + .. templatetag:: load ``load`` diff --git a/docs/releases/3.1.txt b/docs/releases/3.1.txt index df7aad8dd2..444cba34dc 100644 --- a/docs/releases/3.1.txt +++ b/docs/releases/3.1.txt @@ -322,6 +322,8 @@ Templates and :ttag:`blocktrans` template tags aliases continue to work, and will be retained for the foreseeable future. +* The :ttag:`include` template tag now accepts iterables of template names. + Tests ~~~~~ diff --git a/tests/template_tests/syntax_tests/test_include.py b/tests/template_tests/syntax_tests/test_include.py index b840c676f7..f1fee7d227 100644 --- a/tests/template_tests/syntax_tests/test_include.py +++ b/tests/template_tests/syntax_tests/test_include.py @@ -243,6 +243,26 @@ class IncludeTests(SimpleTestCase): output = outer_tmpl.render(ctx) self.assertEqual(output, 'This worked!') + def test_include_template_iterable(self): + engine = Engine.get_default() + outer_temp = engine.from_string('{% include var %}') + tests = [ + ('admin/fail.html', 'index.html'), + ['admin/fail.html', 'index.html'], + ] + for template_names in tests: + with self.subTest(template_names): + output = outer_temp.render(Context({'var': template_names})) + self.assertEqual(output, 'index\n') + + def test_include_template_none(self): + engine = Engine.get_default() + outer_temp = engine.from_string('{% include var %}') + ctx = Context({'var': None}) + msg = 'No template names provided' + with self.assertRaisesMessage(TemplateDoesNotExist, msg): + outer_temp.render(ctx) + def test_include_from_loader_get_template(self): tmpl = loader.get_template('include_tpl.html') # {% include tmpl %} output = tmpl.render({'tmpl': loader.get_template('index.html')})