diff --git a/django/template/loader_tags.py b/django/template/loader_tags.py
index 7b4de9a..2655b88 100644
a
|
b
|
from collections import defaultdict
|
6 | 6 | from django.utils.deprecation import RemovedInDjango21Warning |
7 | 7 | from django.utils.safestring import mark_safe |
8 | 8 | |
| 9 | from . import engines |
9 | 10 | from .base import ( |
10 | 11 | Node, Template, TemplateSyntaxError, TextNode, Variable, token_kwargs, |
11 | 12 | ) |
… |
… |
BLOCK_CONTEXT_KEY = 'block_context'
|
18 | 19 | logger = logging.getLogger('django.template') |
19 | 20 | |
20 | 21 | |
| 22 | class DifferentTemplateEngineError(Exception): |
| 23 | pass |
| 24 | |
| 25 | |
21 | 26 | class BlockContext: |
22 | 27 | def __init__(self): |
23 | 28 | # Dictionary of FIFO queues. |
… |
… |
class IncludeNode(Node):
|
183 | 188 | # Use the base.Template of a backends.django.Template. |
184 | 189 | elif hasattr(template, 'template'): |
185 | 190 | template = template.template |
| 191 | |
| 192 | if context.template.engine != template.engine: |
| 193 | raise DifferentTemplateEngineError( |
| 194 | 'Included template %r is from engine %r but parent template ' |
| 195 | '%r is from engine %r. Including a template from a ' |
| 196 | 'different engine is prohibited.' % ( |
| 197 | template.name, |
| 198 | engines.get_name_from_engine(template.engine), |
| 199 | context.template.name, |
| 200 | engines.get_name_from_engine(context.template.engine), |
| 201 | ) |
| 202 | ) |
186 | 203 | values = { |
187 | 204 | name: var.resolve(context) |
188 | 205 | for name, var in self.extra_context.items() |
… |
… |
class IncludeNode(Node):
|
191 | 208 | return template.render(context.new(values)) |
192 | 209 | with context.push(**values): |
193 | 210 | return template.render(context) |
| 211 | except DifferentTemplateEngineError: |
| 212 | raise |
194 | 213 | except Exception as e: |
195 | 214 | if context.template.engine.debug: |
196 | 215 | raise |
diff --git a/django/template/utils.py b/django/template/utils.py
index fde2f64..fad3e90 100644
a
|
b
|
class EngineHandler:
|
87 | 87 | def all(self): |
88 | 88 | return [self[alias] for alias in self] |
89 | 89 | |
| 90 | def get_name_from_engine(self, engine): |
| 91 | try: |
| 92 | return next(e.name for e in self.all() if e.engine is engine) |
| 93 | except StopIteration: |
| 94 | return 'unnamed' |
| 95 | |
90 | 96 | |
91 | 97 | @functools.lru_cache() |
92 | 98 | def get_app_template_dirs(dirname): |
diff --git a/tests/template_tests/syntax_tests/test_include.py b/tests/template_tests/syntax_tests/test_include.py
index 2b08f3f..28b20e0 100644
a
|
b
|
import warnings
|
3 | 3 | from django.template import ( |
4 | 4 | Context, Engine, TemplateDoesNotExist, TemplateSyntaxError, loader, |
5 | 5 | ) |
| 6 | from django.template.loader_tags import DifferentTemplateEngineError |
6 | 7 | from django.test import SimpleTestCase, ignore_warnings |
7 | 8 | from django.utils.deprecation import RemovedInDjango21Warning |
8 | 9 | |
… |
… |
class IncludeTests(SimpleTestCase):
|
282 | 283 | output = tmpl.render({'tmpl': loader.get_template('index.html')}) |
283 | 284 | self.assertEqual(output, 'index\n\n') |
284 | 285 | |
| 286 | def test_include_from_other_engine(self): |
| 287 | engine = Engine() |
| 288 | ctx = Context({'tmpl': loader.get_template('index.html')}) |
| 289 | outer_tmpl = engine.from_string('{% include tmpl %}') |
| 290 | msg = ( |
| 291 | "Included template 'index.html' is from engine 'django' but parent " |
| 292 | "template None is from engine 'unnamed'. Including a template from " |
| 293 | "a different engine is prohibited." |
| 294 | ) |
| 295 | with self.assertRaisesMessage(DifferentTemplateEngineError, msg): |
| 296 | outer_tmpl.render(ctx) |
| 297 | |
285 | 298 | def test_include_immediate_missing(self): |
286 | 299 | """ |
287 | 300 | #16417 -- Include tags pointing to missing templates should not raise |