summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorPreston Timmons <prestontimmons@gmail.com>2015-03-03 15:48:26 -0600
committerPreston Timmons <prestontimmons@gmail.com>2015-04-22 14:49:00 -0500
commitfc2147152637e21bc73f991b50fa06254af02739 (patch)
treec9f80c73aabf680e67c6646361d8de3a29a62f93 /django
parent1b1b58bc7b2917b203ee3eb3b9e8b3a1fafaee64 (diff)
Fixed #15053 -- Enabled recursive template loading.
Diffstat (limited to 'django')
-rw-r--r--django/template/__init__.py4
-rw-r--r--django/template/base.py38
-rw-r--r--django/template/engine.py34
-rw-r--r--django/template/loader.py28
-rw-r--r--django/template/loader_tags.py36
-rw-r--r--django/template/loaders/base.py80
-rw-r--r--django/template/loaders/cached.py96
-rw-r--r--django/template/loaders/eggs.py52
-rw-r--r--django/template/loaders/filesystem.py44
-rw-r--r--django/template/loaders/locmem.py23
10 files changed, 347 insertions, 88 deletions
diff --git a/django/template/__init__.py b/django/template/__init__.py
index b2566decad..780dbc29fc 100644
--- a/django/template/__init__.py
+++ b/django/template/__init__.py
@@ -59,8 +59,8 @@ from .base import (TemplateDoesNotExist, TemplateSyntaxError, # NOQA
from .context import ContextPopException # NOQA
# Template parts
-from .base import (Context, Node, NodeList, RequestContext, # NOQA
- StringOrigin, Template, Variable)
+from .base import (Context, Node, NodeList, Origin, RequestContext, # NOQA
+ Template, Variable)
# Deprecated in Django 1.8, will be removed in Django 2.0.
from .base import resolve_variable # NOQA
diff --git a/django/template/base.py b/django/template/base.py
index 19b6b76d45..e1849999f2 100644
--- a/django/template/base.py
+++ b/django/template/base.py
@@ -134,7 +134,15 @@ class TemplateSyntaxError(Exception):
class TemplateDoesNotExist(Exception):
- pass
+ """
+ This exception is used when template loaders are unable to find a
+ template. The tried argument is an optional list of tuples containing
+ (origin, status), where origin is an Origin object and status is a string
+ with the reason the template wasn't found.
+ """
+ def __init__(self, msg, tried=None):
+ self.tried = tried or []
+ super(TemplateDoesNotExist, self).__init__(msg)
class TemplateEncodingError(Exception):
@@ -157,23 +165,29 @@ class InvalidTemplateLibrary(Exception):
class Origin(object):
- def __init__(self, name):
+ def __init__(self, name, template_name=None, loader=None):
self.name = name
-
- def reload(self):
- raise NotImplementedError('subclasses of Origin must provide a reload() method')
+ self.template_name = template_name
+ self.loader = loader
def __str__(self):
return self.name
+ def __eq__(self, other):
+ if not isinstance(other, Origin):
+ return False
-class StringOrigin(Origin):
- def __init__(self, source):
- super(StringOrigin, self).__init__(UNKNOWN_SOURCE)
- self.source = source
+ return (
+ self.name == other.name and
+ self.loader == other.loader
+ )
- def reload(self):
- return self.source
+ @property
+ def loader_name(self):
+ if self.loader:
+ return '%s.%s' % (
+ self.loader.__module__, self.loader.__class__.__name__,
+ )
class Template(object):
@@ -191,7 +205,7 @@ class Template(object):
from .engine import Engine
engine = Engine.get_default()
if origin is None:
- origin = StringOrigin(template_string)
+ origin = Origin(UNKNOWN_SOURCE)
self.name = name
self.origin = origin
self.engine = engine
diff --git a/django/template/engine.py b/django/template/engine.py
index ed398b217b..90015dfacc 100644
--- a/django/template/engine.py
+++ b/django/template/engine.py
@@ -124,15 +124,25 @@ class Engine(object):
raise ImproperlyConfigured(
"Invalid value in template loaders configuration: %r" % loader)
- def find_template(self, name, dirs=None):
+ def find_template(self, name, dirs=None, skip=None):
+ tried = []
for loader in self.template_loaders:
- try:
- source, display_name = loader(name, dirs)
- origin = self.make_origin(display_name, loader, name, dirs)
- return source, origin
- except TemplateDoesNotExist:
- pass
- raise TemplateDoesNotExist(name)
+ if loader.supports_recursion:
+ try:
+ template = loader.get_template(
+ name, template_dirs=dirs, skip=skip,
+ )
+ return template, template.origin
+ except TemplateDoesNotExist as e:
+ tried.extend(e.tried)
+ else:
+ # RemovedInDjango21Warning: Use old api for non-recursive
+ # loaders.
+ try:
+ return loader(name, dirs)
+ except TemplateDoesNotExist:
+ pass
+ raise TemplateDoesNotExist(name, tried=tried)
def from_string(self, template_code):
"""
@@ -234,11 +244,3 @@ class Engine(object):
continue
# If we get here, none of the templates could be loaded
raise TemplateDoesNotExist(', '.join(not_found))
-
- def make_origin(self, display_name, loader, name, dirs):
- if self.debug and display_name:
- # Inner import to avoid circular dependency
- from .loader import LoaderOrigin
- return LoaderOrigin(display_name, loader, name, dirs)
- else:
- return None
diff --git a/django/template/loader.py b/django/template/loader.py
index 182d9aacf6..2f9afd3071 100644
--- a/django/template/loader.py
+++ b/django/template/loader.py
@@ -4,25 +4,20 @@ from django.utils.deprecation import RemovedInDjango20Warning
from . import engines
from .backends.django import DjangoTemplates
-from .base import Origin, TemplateDoesNotExist
+from .base import TemplateDoesNotExist
from .engine import (
_context_instance_undefined, _dictionary_undefined, _dirs_undefined,
)
from .loaders import base
-class LoaderOrigin(Origin):
- def __init__(self, display_name, loader, name, dirs):
- super(LoaderOrigin, self).__init__(display_name)
- self.loader, self.loadname, self.dirs = loader, name, dirs
-
-
def get_template(template_name, dirs=_dirs_undefined, using=None):
"""
Loads and returns a template for the given name.
Raises TemplateDoesNotExist if no such template exists.
"""
+ tried = []
engines = _engine_list(using)
for engine in engines:
try:
@@ -37,10 +32,10 @@ def get_template(template_name, dirs=_dirs_undefined, using=None):
stacklevel=2)
else:
return engine.get_template(template_name)
- except TemplateDoesNotExist:
- pass
+ except TemplateDoesNotExist as e:
+ tried.extend(e.tried)
- raise TemplateDoesNotExist(template_name)
+ raise TemplateDoesNotExist(template_name, tried=tried)
def select_template(template_name_list, dirs=_dirs_undefined, using=None):
@@ -51,6 +46,7 @@ def select_template(template_name_list, dirs=_dirs_undefined, using=None):
Raises TemplateDoesNotExist if no such template exists.
"""
+ tried = []
engines = _engine_list(using)
for template_name in template_name_list:
for engine in engines:
@@ -66,11 +62,11 @@ def select_template(template_name_list, dirs=_dirs_undefined, using=None):
stacklevel=2)
else:
return engine.get_template(template_name)
- except TemplateDoesNotExist:
- pass
+ except TemplateDoesNotExist as e:
+ tried.extend(e.tried)
if template_name_list:
- raise TemplateDoesNotExist(', '.join(template_name_list))
+ raise TemplateDoesNotExist(', '.join(template_name_list), tried=tried)
else:
raise TemplateDoesNotExist("No template names provided")
@@ -96,6 +92,7 @@ def render_to_string(template_name, context=None,
return template.render(context, request)
else:
+ tried = []
# Some deprecated arguments were passed - use the legacy code path
for engine in _engine_list(using):
try:
@@ -126,13 +123,14 @@ def render_to_string(template_name, context=None,
"Skipping template backend %s because its render_to_string "
"method doesn't support the dictionary argument." %
engine.name, stacklevel=2)
- except TemplateDoesNotExist:
+ except TemplateDoesNotExist as e:
+ tried.extend(e.tried)
continue
if template_name:
if isinstance(template_name, (list, tuple)):
template_name = ', '.join(template_name)
- raise TemplateDoesNotExist(template_name)
+ raise TemplateDoesNotExist(template_name, tried=tried)
else:
raise TemplateDoesNotExist("No template names provided")
diff --git a/django/template/loader_tags.py b/django/template/loader_tags.py
index 398914b6b7..3b846a4099 100644
--- a/django/template/loader_tags.py
+++ b/django/template/loader_tags.py
@@ -82,6 +82,7 @@ class BlockNode(Node):
class ExtendsNode(Node):
must_be_first = True
+ context_key = 'extends_context'
def __init__(self, nodelist, parent_name, template_dirs=None):
self.nodelist = nodelist
@@ -92,6 +93,39 @@ class ExtendsNode(Node):
def __repr__(self):
return '<ExtendsNode: extends %s>' % self.parent_name.token
+ def find_template(self, template_name, context):
+ """
+ This is a wrapper around engine.find_template(). A history is kept in
+ the render_context attribute between successive extends calls and
+ passed as the skip argument. This enables extends to work recursively
+ without extending the same template twice.
+ """
+ # RemovedInDjango21Warning: If any non-recursive loaders are installed
+ # do a direct template lookup. If the same template name appears twice,
+ # raise an exception to avoid system recursion.
+ for loader in context.template.engine.template_loaders:
+ if not loader.supports_recursion:
+ history = context.render_context.setdefault(
+ self.context_key, [context.template.origin.template_name],
+ )
+ if template_name in history:
+ raise ExtendsError(
+ "Cannot extend templates recursively when using "
+ "non-recursive template loaders",
+ )
+ template = context.template.engine.get_template(template_name)
+ history.append(template_name)
+ return template
+
+ history = context.render_context.setdefault(
+ self.context_key, [context.template.origin],
+ )
+ template, origin = context.template.engine.find_template(
+ template_name, skip=history,
+ )
+ history.append(origin)
+ return template
+
def get_parent(self, context):
parent = self.parent_name.resolve(context)
if not parent:
@@ -107,7 +141,7 @@ class ExtendsNode(Node):
if isinstance(getattr(parent, 'template', None), Template):
# parent is a django.template.backends.django.Template
return parent.template
- return context.template.engine.get_template(parent)
+ return self.find_template(parent, context)
def render(self, context):
compiled_parent = self.get_parent(context)
diff --git a/django/template/loaders/base.py b/django/template/loaders/base.py
index 66c2986343..b848bc05ce 100644
--- a/django/template/loaders/base.py
+++ b/django/template/loaders/base.py
@@ -1,4 +1,8 @@
-from django.template.base import Template, TemplateDoesNotExist
+import warnings
+from inspect import getargspec
+
+from django.template.base import Origin, Template, TemplateDoesNotExist
+from django.utils.deprecation import RemovedInDjango21Warning
class Loader(object):
@@ -9,15 +13,54 @@ class Loader(object):
self.engine = engine
def __call__(self, template_name, template_dirs=None):
+ # RemovedInDjango21Warning: Allow loaders to be called like functions.
return self.load_template(template_name, template_dirs)
+ def get_template(self, template_name, template_dirs=None, skip=None):
+ """
+ Calls self.get_template_sources() and returns a Template object for
+ the first template matching template_name. If skip is provided,
+ template origins in skip are ignored. This is used to avoid recursion
+ during template extending.
+ """
+ tried = []
+
+ args = [template_name]
+ # RemovedInDjango21Warning: Add template_dirs for compatibility with
+ # old loaders
+ if 'template_dirs' in getargspec(self.get_template_sources)[0]:
+ args.append(template_dirs)
+
+ for origin in self.get_template_sources(*args):
+ if skip is not None and origin in skip:
+ tried.append((origin, 'Skipped'))
+ continue
+
+ try:
+ contents = self.get_contents(origin)
+ except TemplateDoesNotExist:
+ tried.append((origin, 'Source does not exist'))
+ continue
+ else:
+ return Template(
+ contents, origin, origin.template_name, self.engine,
+ )
+
+ raise TemplateDoesNotExist(template_name, tried=tried)
+
def load_template(self, template_name, template_dirs=None):
+ warnings.warn(
+ 'The load_template() method is deprecated. Use get_template() '
+ 'instead.', RemovedInDjango21Warning,
+ )
source, display_name = self.load_template_source(
- template_name, template_dirs)
- origin = self.engine.make_origin(
- display_name, self.load_template_source,
- template_name, template_dirs)
-
+ template_name, template_dirs,
+ )
+ origin = Origin(
+ name=display_name,
+ template_name=template_name,
+ loader=self,
+ )
try:
template = Template(source, origin, template_name, self.engine)
except TemplateDoesNotExist:
@@ -29,14 +72,23 @@ class Loader(object):
else:
return template, None
- def load_template_source(self, template_name, template_dirs=None):
+ def get_template_sources(self, template_name):
"""
- Returns a tuple containing the source and origin for the given
+ An iterator that yields possible matching template paths for a
template name.
"""
raise NotImplementedError(
- "subclasses of Loader must provide "
- "a load_template_source() method")
+ 'subclasses of Loader must provide a get_template_sources() method'
+ )
+
+ def load_template_source(self, template_name, template_dirs=None):
+ """
+ RemovedInDjango21Warning: Returns a tuple containing the source and
+ origin for the given template name.
+ """
+ raise NotImplementedError(
+ 'subclasses of Loader must provide a load_template_source() method'
+ )
def reset(self):
"""
@@ -44,3 +96,11 @@ class Loader(object):
templates or cached loader modules).
"""
pass
+
+ @property
+ def supports_recursion(self):
+ """
+ RemovedInDjango21Warning: This is an internal property used by the
+ ExtendsNode during the deprecation of non-recursive loaders.
+ """
+ return hasattr(self, 'get_contents')
diff --git a/django/template/loaders/cached.py b/django/template/loaders/cached.py
index 188268e92e..b543a83026 100644
--- a/django/template/loaders/cached.py
+++ b/django/template/loaders/cached.py
@@ -4,8 +4,11 @@ to load templates from them in order, caching the result.
"""
import hashlib
+import warnings
+from inspect import getargspec
-from django.template.base import Template, TemplateDoesNotExist
+from django.template.base import Origin, Template, TemplateDoesNotExist
+from django.utils.deprecation import RemovedInDjango21Warning
from django.utils.encoding import force_bytes
from .base import Loader as BaseLoader
@@ -15,20 +18,84 @@ class Loader(BaseLoader):
def __init__(self, engine, loaders):
self.template_cache = {}
- self.find_template_cache = {}
+ self.find_template_cache = {} # RemovedInDjango21Warning
+ self.get_template_cache = {}
self.loaders = engine.get_template_loaders(loaders)
super(Loader, self).__init__(engine)
- def cache_key(self, template_name, template_dirs):
- if template_dirs:
- # If template directories were specified, use a hash to differentiate
- return '-'.join([template_name, hashlib.sha1(force_bytes('|'.join(template_dirs))).hexdigest()])
+ def get_contents(self, origin):
+ return origin.loader.get_contents(origin)
+
+ def get_template(self, template_name, template_dirs=None, skip=None):
+ key = self.cache_key(template_name, template_dirs, skip)
+ cached = self.get_template_cache.get(key)
+ if cached:
+ if isinstance(cached, TemplateDoesNotExist):
+ raise cached
+ return cached
+
+ try:
+ template = super(Loader, self).get_template(
+ template_name, template_dirs, skip,
+ )
+ except TemplateDoesNotExist as e:
+ self.get_template_cache[key] = e
+ raise
else:
- return template_name
+ self.get_template_cache[key] = template
+
+ return template
+
+ def get_template_sources(self, template_name, template_dirs=None):
+ for loader in self.loaders:
+ args = [template_name]
+ # RemovedInDjango21Warning: Add template_dirs for compatibility
+ # with old loaders
+ if 'template_dirs' in getargspec(loader.get_template_sources)[0]:
+ args.append(template_dirs)
+ for origin in loader.get_template_sources(*args):
+ yield origin
+
+ def cache_key(self, template_name, template_dirs, skip=None):
+ """
+ Generate a cache key for the template name, dirs, and skip.
+
+ If skip is provided, only origins that match template_name are included
+ in the cache key. This ensures each template is only parsed and cached
+ once if contained in different extend chains like:
+
+ x -> a -> a
+ y -> a -> a
+ z -> a -> a
+ """
+ dirs_prefix = ''
+ skip_prefix = ''
+
+ if skip:
+ matching = [origin.name for origin in skip if origin.template_name == template_name]
+ if matching:
+ skip_prefix = self.generate_hash(matching)
+
+ if template_dirs:
+ dirs_prefix = self.generate_hash(template_dirs)
+
+ return ("%s-%s-%s" % (template_name, skip_prefix, dirs_prefix)).strip('-')
+
+ def generate_hash(self, values):
+ return hashlib.sha1(force_bytes('|'.join(values))).hexdigest()
+
+ @property
+ def supports_recursion(self):
+ """
+ RemovedInDjango21Warning: This is an internal property used by the
+ ExtendsNode during the deprecation of non-recursive loaders.
+ """
+ return all(hasattr(loader, 'get_contents') for loader in self.loaders)
def find_template(self, name, dirs=None):
"""
- Helper method. Lookup the template :param name: in all the configured loaders
+ RemovedInDjango21Warning: An internal method to lookup the template
+ name in all the configured loaders.
"""
key = self.cache_key(name, dirs)
try:
@@ -41,7 +108,11 @@ class Loader(BaseLoader):
except TemplateDoesNotExist:
pass
else:
- origin = self.engine.make_origin(display_name, loader, name, dirs)
+ origin = Origin(
+ name=display_name,
+ template_name=name,
+ loader=loader,
+ )
result = template, origin
break
self.find_template_cache[key] = result
@@ -52,6 +123,10 @@ class Loader(BaseLoader):
raise TemplateDoesNotExist(name)
def load_template(self, template_name, template_dirs=None):
+ warnings.warn(
+ 'The load_template() method is deprecated. Use get_template() '
+ 'instead.', RemovedInDjango21Warning,
+ )
key = self.cache_key(template_name, template_dirs)
template_tuple = self.template_cache.get(key)
# A cached previous failure:
@@ -74,4 +149,5 @@ class Loader(BaseLoader):
def reset(self):
"Empty the template cache."
self.template_cache.clear()
- self.find_template_cache.clear()
+ self.find_template_cache.clear() # RemovedInDjango21Warning
+ self.get_template_cache.clear()
diff --git a/django/template/loaders/eggs.py b/django/template/loaders/eggs.py
index 14a586877d..9dc291e485 100644
--- a/django/template/loaders/eggs.py
+++ b/django/template/loaders/eggs.py
@@ -1,9 +1,12 @@
# Wrapper for loading templates from eggs via pkg_resources.resource_string.
from __future__ import unicode_literals
+import warnings
+
from django.apps import apps
-from django.template.base import TemplateDoesNotExist
+from django.template.base import Origin, TemplateDoesNotExist
from django.utils import six
+from django.utils.deprecation import RemovedInDjango21Warning
from .base import Loader as BaseLoader
@@ -13,6 +16,14 @@ except ImportError:
resource_string = None
+class EggOrigin(Origin):
+
+ def __init__(self, app_name, pkg_name, *args, **kwargs):
+ self.app_name = app_name
+ self.pkg_name = pkg_name
+ return super(EggOrigin, self).__init__(*args, **kwargs)
+
+
class Loader(BaseLoader):
def __init__(self, engine):
@@ -20,19 +31,42 @@ class Loader(BaseLoader):
raise RuntimeError("Setuptools must be installed to use the egg loader")
super(Loader, self).__init__(engine)
+ def get_contents(self, origin):
+ try:
+ source = resource_string(origin.app_name, origin.pkg_name)
+ except:
+ raise TemplateDoesNotExist(origin)
+
+ if six.PY2:
+ source = source.decode(self.engine.file_charset)
+
+ return source
+
+ def get_template_sources(self, template_name):
+ pkg_name = 'templates/' + template_name
+ for app_config in apps.get_app_configs():
+ yield EggOrigin(
+ app_name=app_config.name,
+ pkg_name=pkg_name,
+ name="egg:%s:%s" % (app_config.name, pkg_name),
+ template_name=template_name,
+ loader=self,
+ )
+
def load_template_source(self, template_name, template_dirs=None):
"""
Loads templates from Python eggs via pkg_resource.resource_string.
For every installed app, it tries to get the resource (app, template_name).
"""
- pkg_name = 'templates/' + template_name
- for app_config in apps.get_app_configs():
+ warnings.warn(
+ 'The load_template_sources() method is deprecated. Use '
+ 'get_template() or get_contents() instead.',
+ RemovedInDjango21Warning,
+ )
+ for origin in self.get_template_sources(template_name):
try:
- resource = resource_string(app_config.name, pkg_name)
- except Exception:
- continue
- if six.PY2:
- resource = resource.decode(self.engine.file_charset)
- return (resource, 'egg:%s:%s' % (app_config.name, pkg_name))
+ return self.get_contents(origin), origin.name
+ except TemplateDoesNotExist:
+ pass
raise TemplateDoesNotExist(template_name)
diff --git a/django/template/loaders/filesystem.py b/django/template/loaders/filesystem.py
index 74801efe56..e1ed0ba5e0 100644
--- a/django/template/loaders/filesystem.py
+++ b/django/template/loaders/filesystem.py
@@ -4,10 +4,12 @@ Wrapper for loading templates from the filesystem.
import errno
import io
+import warnings
from django.core.exceptions import SuspiciousFileOperation
-from django.template.base import TemplateDoesNotExist
+from django.template.base import Origin, TemplateDoesNotExist
from django.utils._os import safe_join
+from django.utils.deprecation import RemovedInDjango21Warning
from .base import Loader as BaseLoader
@@ -17,28 +19,46 @@ class Loader(BaseLoader):
def get_dirs(self):
return self.engine.dirs
+ def get_contents(self, origin):
+ try:
+ with io.open(origin.name, encoding=self.engine.file_charset) as fp:
+ return fp.read()
+ except IOError as e:
+ if e.errno == errno.ENOENT:
+ raise TemplateDoesNotExist(origin)
+ raise
+
def get_template_sources(self, template_name, template_dirs=None):
"""
- Returns the absolute paths to "template_name", when appended to each
- directory in "template_dirs". Any paths that don't lie inside one of the
- template dirs are excluded from the result set, for security reasons.
+ Return an Origin object pointing to an absolute path in each directory
+ in template_dirs. For security reasons, if a path doesn't lie inside
+ one of the template_dirs it is excluded from the result set.
"""
if not template_dirs:
template_dirs = self.get_dirs()
for template_dir in template_dirs:
try:
- yield safe_join(template_dir, template_name)
+ name = safe_join(template_dir, template_name)
except SuspiciousFileOperation:
# The joined path was located outside of this template_dir
# (it might be inside another one, so this isn't fatal).
- pass
+ continue
+
+ yield Origin(
+ name=name,
+ template_name=template_name,
+ loader=self,
+ )
def load_template_source(self, template_name, template_dirs=None):
- for filepath in self.get_template_sources(template_name, template_dirs):
+ warnings.warn(
+ 'The load_template_sources() method is deprecated. Use '
+ 'get_template() or get_contents() instead.',
+ RemovedInDjango21Warning,
+ )
+ for origin in self.get_template_sources(template_name, template_dirs):
try:
- with io.open(filepath, encoding=self.engine.file_charset) as fp:
- return fp.read(), filepath
- except IOError as e:
- if e.errno != errno.ENOENT:
- raise
+ return self.get_contents(origin), origin.name
+ except TemplateDoesNotExist:
+ pass
raise TemplateDoesNotExist(template_name)
diff --git a/django/template/loaders/locmem.py b/django/template/loaders/locmem.py
index 598907bb71..f0ef792b35 100644
--- a/django/template/loaders/locmem.py
+++ b/django/template/loaders/locmem.py
@@ -2,7 +2,10 @@
Wrapper for loading templates from a plain Python dict.
"""
-from django.template.base import TemplateDoesNotExist
+import warnings
+
+from django.template.base import Origin, TemplateDoesNotExist
+from django.utils.deprecation import RemovedInDjango21Warning
from .base import Loader as BaseLoader
@@ -13,7 +16,25 @@ class Loader(BaseLoader):
self.templates_dict = templates_dict
super(Loader, self).__init__(engine)
+ def get_contents(self, origin):
+ try:
+ return self.templates_dict[origin.name]
+ except KeyError:
+ raise TemplateDoesNotExist(origin)
+
+ def get_template_sources(self, template_name):
+ yield Origin(
+ name=template_name,
+ template_name=template_name,
+ loader=self,
+ )
+
def load_template_source(self, template_name, template_dirs=None):
+ warnings.warn(
+ 'The load_template_sources() method is deprecated. Use '
+ 'get_template() or get_contents() instead.',
+ RemovedInDjango21Warning,
+ )
try:
return self.templates_dict[template_name], template_name
except KeyError: