1 # Wrapper for loading templates from storage of some sort (e.g. filesystem, database).
3 # This uses the TEMPLATE_LOADERS setting, which is a list of loaders to use.
4 # Each loader is expected to have this interface:
6 # callable(name, dirs=[])
8 # name is the template name.
9 # dirs is an optional list of directories to search instead of TEMPLATE_DIRS.
11 # The loader should return a tuple of (template_source, path). The path returned
12 # might be shown to the user for debugging purposes, so it should identify where
13 # the template was loaded from.
15 # A loader may return an already-compiled template instead of the actual
16 # template source. In that case the path returned should be None, since the
17 # path information is associated with the template during the compilation,
18 # which has already been done.
20 # Each loader should have an "is_usable" attribute set. This is a boolean that
21 # specifies whether the loader can be used in this Python installation. Each
22 # loader is responsible for setting this when it's initialized.
24 # For example, the eggs loader (which is capable of loading templates from
25 # Python eggs) sets is_usable to False if the "pkg_resources" module isn't
26 # installed, because pkg_resources is necessary to read eggs.
28 from django
.core
.exceptions
import ImproperlyConfigured
29 from django
.template
import Origin
, Template
, Context
, TemplateDoesNotExist
, add_to_builtins
30 from django
.utils
.importlib
import import_module
31 from django
.conf
import settings
33 template_source_loaders
= None
35 class BaseLoader(object):
38 def __init__(self
, *args
, **kwargs
):
41 def __call__(self
, template_name
, template_dirs
=None):
42 return self
.load_template(template_name
, template_dirs
)
44 def load_template(self
, template_name
, template_dirs
=None):
45 source
, display_name
= self
.load_template_source(template_name
, template_dirs
)
46 origin
= make_origin(display_name
, self
.load_template_source
, template_name
, template_dirs
)
48 template
= get_template_from_string(source
, origin
, template_name
)
50 except TemplateDoesNotExist
:
51 # If compiling the template we found raises TemplateDoesNotExist, back off to
52 # returning the source and display name for the template we were asked to load.
53 # This allows for correct identification (later) of the actual template that does
55 return source
, display_name
57 def load_template_source(self
, template_name
, template_dirs
=None):
59 Returns a tuple containing the source and origin for the given template
63 raise NotImplementedError
67 Resets any state maintained by the loader instance (e.g., cached
68 templates or cached loader modules).
73 class LoaderOrigin(Origin
):
74 def __init__(self
, display_name
, loader
, name
, dirs
):
75 super(LoaderOrigin
, self
).__init
__(display_name
)
76 self
.loader
, self
.loadname
, self
.dirs
= loader
, name
, dirs
79 return self
.loader(self
.loadname
, self
.dirs
)[0]
81 def make_origin(display_name
, loader
, name
, dirs
):
82 if settings
.TEMPLATE_DEBUG
and display_name
:
83 return LoaderOrigin(display_name
, loader
, name
, dirs
)
87 def find_template_loader(loader
):
88 if isinstance(loader
, (tuple, list)):
89 loader
, args
= loader
[0], loader
[1:]
92 if isinstance(loader
, basestring
):
93 module
, attr
= loader
.rsplit('.', 1)
95 mod
= import_module(module
)
96 except ImportError, e
:
97 raise ImproperlyConfigured('Error importing template source loader %s: "%s"' % (loader
, e
))
99 TemplateLoader
= getattr(mod
, attr
)
100 except AttributeError, e
:
101 raise ImproperlyConfigured('Error importing template source loader %s: "%s"' % (loader
, e
))
103 if hasattr(TemplateLoader
, 'load_template_source'):
104 func
= TemplateLoader(*args
)
106 # Try loading module the old way - string is full path to callable
108 raise ImproperlyConfigured("Error importing template source loader %s - can't pass arguments to function-based loader." % loader
)
109 func
= TemplateLoader
111 if not func
.is_usable
:
113 warnings
.warn("Your TEMPLATE_LOADERS setting includes %r, but your Python installation doesn't support that type of template loading. Consider removing that line from TEMPLATE_LOADERS." % loader
)
118 raise ImproperlyConfigured('Loader does not define a "load_template" callable template source loader')
120 def find_template(name
, dirs
=None):
121 # Calculate template_source_loaders the first time the function is executed
122 # because putting this logic in the module-level namespace may cause
123 # circular import errors. See Django ticket #1292.
124 global template_source_loaders
125 if template_source_loaders
is None:
127 for loader_name
in settings
.TEMPLATE_LOADERS
:
128 loader
= find_template_loader(loader_name
)
129 if loader
is not None:
130 loaders
.append(loader
)
131 template_source_loaders
= tuple(loaders
)
132 for loader
in template_source_loaders
:
134 source
, display_name
= loader(name
, dirs
)
135 return (source
, make_origin(display_name
, loader
, name
, dirs
))
136 except TemplateDoesNotExist
:
138 raise TemplateDoesNotExist(name
)
140 def find_template_source(name
, dirs
=None):
141 # For backward compatibility
144 "`django.template.loaders.find_template_source` is deprecated; use `django.template.loaders.find_template` instead.",
145 PendingDeprecationWarning
147 template
, origin
= find_template(name
, dirs
)
148 if hasattr(template
, 'render'):
149 raise Exception("Found a compiled template that is incompatible with the deprecated `django.template.loaders.find_template_source` function.")
150 return template
, origin
152 def get_template(template_name
):
154 Returns a compiled Template object for the given template name,
155 handling template inheritance recursively.
157 template
, origin
= find_template(template_name
)
158 if not hasattr(template
, 'render'):
159 # template needs to be compiled
160 template
= get_template_from_string(template
, origin
, template_name
)
163 def get_template_from_string(source
, origin
=None, name
=None):
165 Returns a compiled Template object for the given template code,
166 handling template inheritance recursively.
168 return Template(source
, origin
, name
)
170 def render_to_string(template_name
, dictionary
=None, context_instance
=None):
172 Loads the given template_name and renders it with the given dictionary as
173 context. The template_name may be a string to load a single template using
174 get_template, or it may be a tuple to use select_template to find one of
175 the templates in the list. Returns a string.
177 dictionary
= dictionary
or {}
178 if isinstance(template_name
, (list, tuple)):
179 t
= select_template(template_name
)
181 t
= get_template(template_name
)
183 context_instance
.update(dictionary
)
185 context_instance
= Context(dictionary
)
186 return t
.render(context_instance
)
188 def select_template(template_name_list
):
189 "Given a list of template names, returns the first that can be loaded."
190 for template_name
in template_name_list
:
192 return get_template(template_name
)
193 except TemplateDoesNotExist
:
195 # If we get here, none of the templates could be loaded
196 raise TemplateDoesNotExist(', '.join(template_name_list
))
198 add_to_builtins('django.template.loader_tags')