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
.base
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 get_template(template_name
):
142 Returns a compiled Template object for the given template name,
143 handling template inheritance recursively.
145 template
, origin
= find_template(template_name
)
146 if not hasattr(template
, 'render'):
147 # template needs to be compiled
148 template
= get_template_from_string(template
, origin
, template_name
)
151 def get_template_from_string(source
, origin
=None, name
=None):
153 Returns a compiled Template object for the given template code,
154 handling template inheritance recursively.
156 return Template(source
, origin
, name
)
158 def render_to_string(template_name
, dictionary
=None, context_instance
=None):
160 Loads the given template_name and renders it with the given dictionary as
161 context. The template_name may be a string to load a single template using
162 get_template, or it may be a tuple to use select_template to find one of
163 the templates in the list. Returns a string.
165 dictionary
= dictionary
or {}
166 if isinstance(template_name
, (list, tuple)):
167 t
= select_template(template_name
)
169 t
= get_template(template_name
)
170 if not context_instance
:
171 return t
.render(Context(dictionary
))
172 # Add the dictionary to the context stack, ensuring it gets removed again
173 # to keep the context_instance in the same state it started in.
174 context_instance
.update(dictionary
)
176 return t
.render(context_instance
)
178 context_instance
.pop()
180 def select_template(template_name_list
):
181 "Given a list of template names, returns the first that can be loaded."
182 if not template_name_list
:
183 raise TemplateDoesNotExist("No template names provided")
185 for template_name
in template_name_list
:
187 return get_template(template_name
)
188 except TemplateDoesNotExist
, e
:
189 if e
.args
[0] not in not_found
:
190 not_found
.append(e
.args
[0])
192 # If we get here, none of the templates could be loaded
193 raise TemplateDoesNotExist(', '.join(not_found
))
195 add_to_builtins('django.template.loader_tags')