web/lib/django/template/loader.py
changeset 38 77b6da96e6f1
equal deleted inserted replaced
37:8d941af65caf 38:77b6da96e6f1
       
     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 # 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.
       
    19 #
       
    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.
       
    23 #
       
    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.
       
    27 
       
    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
       
    32 
       
    33 template_source_loaders = None
       
    34 
       
    35 class BaseLoader(object):
       
    36     is_usable = False
       
    37 
       
    38     def __init__(self, *args, **kwargs):
       
    39         pass
       
    40 
       
    41     def __call__(self, template_name, template_dirs=None):
       
    42         return self.load_template(template_name, template_dirs)
       
    43 
       
    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)
       
    47         try:
       
    48             template = get_template_from_string(source, origin, template_name)
       
    49             return template, None
       
    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
       
    54             # not exist.
       
    55             return source, display_name
       
    56 
       
    57     def load_template_source(self, template_name, template_dirs=None):
       
    58         """
       
    59         Returns a tuple containing the source and origin for the given template
       
    60         name.
       
    61 
       
    62         """
       
    63         raise NotImplementedError
       
    64 
       
    65     def reset(self):
       
    66         """
       
    67         Resets any state maintained by the loader instance (e.g., cached
       
    68         templates or cached loader modules).
       
    69 
       
    70         """
       
    71         pass
       
    72 
       
    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
       
    77 
       
    78     def reload(self):
       
    79         return self.loader(self.loadname, self.dirs)[0]
       
    80 
       
    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)
       
    84     else:
       
    85         return None
       
    86 
       
    87 def find_template_loader(loader):
       
    88     if isinstance(loader, (tuple, list)):
       
    89         loader, args = loader[0], loader[1:]
       
    90     else:
       
    91         args = []
       
    92     if isinstance(loader, basestring):
       
    93         module, attr = loader.rsplit('.', 1)
       
    94         try:
       
    95             mod = import_module(module)
       
    96         except ImportError, e:
       
    97             raise ImproperlyConfigured('Error importing template source loader %s: "%s"' % (loader, e))
       
    98         try:
       
    99             TemplateLoader = getattr(mod, attr)
       
   100         except AttributeError, e:
       
   101             raise ImproperlyConfigured('Error importing template source loader %s: "%s"' % (loader, e))
       
   102 
       
   103         if hasattr(TemplateLoader, 'load_template_source'):
       
   104             func = TemplateLoader(*args)
       
   105         else:
       
   106             # Try loading module the old way - string is full path to callable
       
   107             if args:
       
   108                 raise ImproperlyConfigured("Error importing template source loader %s - can't pass arguments to function-based loader." % loader)
       
   109             func = TemplateLoader
       
   110 
       
   111         if not func.is_usable:
       
   112             import warnings
       
   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)
       
   114             return None
       
   115         else:
       
   116             return func
       
   117     else:
       
   118         raise ImproperlyConfigured('Loader does not define a "load_template" callable template source loader')
       
   119 
       
   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:
       
   126         loaders = []
       
   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:
       
   133         try:
       
   134             source, display_name = loader(name, dirs)
       
   135             return (source, make_origin(display_name, loader, name, dirs))
       
   136         except TemplateDoesNotExist:
       
   137             pass
       
   138     raise TemplateDoesNotExist(name)
       
   139 
       
   140 def find_template_source(name, dirs=None):
       
   141     # For backward compatibility
       
   142     import warnings
       
   143     warnings.warn(
       
   144         "`django.template.loaders.find_template_source` is deprecated; use `django.template.loaders.find_template` instead.",
       
   145         PendingDeprecationWarning
       
   146     )
       
   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
       
   151 
       
   152 def get_template(template_name):
       
   153     """
       
   154     Returns a compiled Template object for the given template name,
       
   155     handling template inheritance recursively.
       
   156     """
       
   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)
       
   161     return template
       
   162 
       
   163 def get_template_from_string(source, origin=None, name=None):
       
   164     """
       
   165     Returns a compiled Template object for the given template code,
       
   166     handling template inheritance recursively.
       
   167     """
       
   168     return Template(source, origin, name)
       
   169 
       
   170 def render_to_string(template_name, dictionary=None, context_instance=None):
       
   171     """
       
   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.
       
   176     """
       
   177     dictionary = dictionary or {}
       
   178     if isinstance(template_name, (list, tuple)):
       
   179         t = select_template(template_name)
       
   180     else:
       
   181         t = get_template(template_name)
       
   182     if context_instance:
       
   183         context_instance.update(dictionary)
       
   184     else:
       
   185         context_instance = Context(dictionary)
       
   186     return t.render(context_instance)
       
   187 
       
   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:
       
   191         try:
       
   192             return get_template(template_name)
       
   193         except TemplateDoesNotExist:
       
   194             continue
       
   195     # If we get here, none of the templates could be loaded
       
   196     raise TemplateDoesNotExist(', '.join(template_name_list))
       
   197 
       
   198 add_to_builtins('django.template.loader_tags')