|
1 # Wrapper for loading templates from storage of some sort (e.g. filesystem, database). |
|
2 # |
|
3 # This uses the TEMPLATE_LOADERS setting, which is a list of loaders to use. |
|
4 # Each loader is expected to have this interface: |
|
5 # |
|
6 # callable(name, dirs=[]) |
|
7 # |
|
8 # name is the template name. |
|
9 # dirs is an optional list of directories to search instead of TEMPLATE_DIRS. |
|
10 # |
|
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. |
|
14 # |
|
15 # Each loader should have an "is_usable" attribute set. This is a boolean that |
|
16 # specifies whether the loader can be used in this Python installation. Each |
|
17 # loader is responsible for setting this when it's initialized. |
|
18 # |
|
19 # For example, the eggs loader (which is capable of loading templates from |
|
20 # Python eggs) sets is_usable to False if the "pkg_resources" module isn't |
|
21 # installed, because pkg_resources is necessary to read eggs. |
|
22 |
|
23 from django.core.exceptions import ImproperlyConfigured |
|
24 from django.template import Origin, Template, Context, TemplateDoesNotExist, add_to_builtins |
|
25 from django.conf import settings |
|
26 |
|
27 template_source_loaders = None |
|
28 |
|
29 class LoaderOrigin(Origin): |
|
30 def __init__(self, display_name, loader, name, dirs): |
|
31 super(LoaderOrigin, self).__init__(display_name) |
|
32 self.loader, self.loadname, self.dirs = loader, name, dirs |
|
33 |
|
34 def reload(self): |
|
35 return self.loader(self.loadname, self.dirs)[0] |
|
36 |
|
37 def make_origin(display_name, loader, name, dirs): |
|
38 if settings.TEMPLATE_DEBUG: |
|
39 return LoaderOrigin(display_name, loader, name, dirs) |
|
40 else: |
|
41 return None |
|
42 |
|
43 def find_template_source(name, dirs=None): |
|
44 # Calculate template_source_loaders the first time the function is executed |
|
45 # because putting this logic in the module-level namespace may cause |
|
46 # circular import errors. See Django ticket #1292. |
|
47 global template_source_loaders |
|
48 if template_source_loaders is None: |
|
49 loaders = [] |
|
50 for path in settings.TEMPLATE_LOADERS: |
|
51 i = path.rfind('.') |
|
52 module, attr = path[:i], path[i+1:] |
|
53 try: |
|
54 mod = __import__(module, globals(), locals(), [attr]) |
|
55 except ImportError, e: |
|
56 raise ImproperlyConfigured, 'Error importing template source loader %s: "%s"' % (module, e) |
|
57 try: |
|
58 func = getattr(mod, attr) |
|
59 except AttributeError: |
|
60 raise ImproperlyConfigured, 'Module "%s" does not define a "%s" callable template source loader' % (module, attr) |
|
61 if not func.is_usable: |
|
62 import warnings |
|
63 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." % path) |
|
64 else: |
|
65 loaders.append(func) |
|
66 template_source_loaders = tuple(loaders) |
|
67 for loader in template_source_loaders: |
|
68 try: |
|
69 source, display_name = loader(name, dirs) |
|
70 return (source, make_origin(display_name, loader, name, dirs)) |
|
71 except TemplateDoesNotExist: |
|
72 pass |
|
73 raise TemplateDoesNotExist, name |
|
74 |
|
75 def get_template(template_name): |
|
76 """ |
|
77 Returns a compiled Template object for the given template name, |
|
78 handling template inheritance recursively. |
|
79 """ |
|
80 source, origin = find_template_source(template_name) |
|
81 template = get_template_from_string(source, origin, template_name) |
|
82 return template |
|
83 |
|
84 def get_template_from_string(source, origin=None, name=None): |
|
85 """ |
|
86 Returns a compiled Template object for the given template code, |
|
87 handling template inheritance recursively. |
|
88 """ |
|
89 return Template(source, origin, name) |
|
90 |
|
91 def render_to_string(template_name, dictionary=None, context_instance=None): |
|
92 """ |
|
93 Loads the given template_name and renders it with the given dictionary as |
|
94 context. The template_name may be a string to load a single template using |
|
95 get_template, or it may be a tuple to use select_template to find one of |
|
96 the templates in the list. Returns a string. |
|
97 """ |
|
98 dictionary = dictionary or {} |
|
99 if isinstance(template_name, (list, tuple)): |
|
100 t = select_template(template_name) |
|
101 else: |
|
102 t = get_template(template_name) |
|
103 if context_instance: |
|
104 context_instance.update(dictionary) |
|
105 else: |
|
106 context_instance = Context(dictionary) |
|
107 return t.render(context_instance) |
|
108 |
|
109 def select_template(template_name_list): |
|
110 "Given a list of template names, returns the first that can be loaded." |
|
111 for template_name in template_name_list: |
|
112 try: |
|
113 return get_template(template_name) |
|
114 except TemplateDoesNotExist: |
|
115 continue |
|
116 # If we get here, none of the templates could be loaded |
|
117 raise TemplateDoesNotExist, ', '.join(template_name_list) |
|
118 |
|
119 add_to_builtins('django.template.loader_tags') |