Project

General

Profile

Download (4.9 KB) Statistics
| Branch: | Tag: | Revision:

root / entrouvert / djommon / multitenant / template_loader.py @ bb762ce4

1
"""
2
Wrapper class that takes a list of template loaders as an argument and attempts
3
to load templates from them in order, caching the result.
4
"""
5

    
6
import hashlib
7
from django.conf import settings
8
from django.core.exceptions import ImproperlyConfigured
9
from django.template.base import TemplateDoesNotExist
10
from django.template.loader import BaseLoader, get_template_from_string, find_template_loader, make_origin
11
from django.utils.encoding import force_bytes
12
from django.utils._os import safe_join
13
from django.db import connection
14

    
15
class CachedLoader(BaseLoader):
16
    is_usable = True
17

    
18
    def __init__(self, loaders):
19
        self.template_cache = {}
20
        self._loaders = loaders
21
        self._cached_loaders = []
22

    
23
    @property
24
    def loaders(self):
25
        # Resolve loaders on demand to avoid circular imports
26
        if not self._cached_loaders:
27
            # Set self._cached_loaders atomically. Otherwise, another thread
28
            # could see an incomplete list. See #17303.
29
            cached_loaders = []
30
            for loader in self._loaders:
31
                cached_loaders.append(find_template_loader(loader))
32
            self._cached_loaders = cached_loaders
33
        return self._cached_loaders
34

    
35
    def find_template(self, name, dirs=None):
36
        for loader in self.loaders:
37
            try:
38
                template, display_name = loader(name, dirs)
39
                return (template, make_origin(display_name, loader, name, dirs))
40
            except TemplateDoesNotExist:
41
                pass
42
        raise TemplateDoesNotExist(name)
43

    
44
    def load_template(self, template_name, template_dirs=None):
45
        if connection.tenant:
46
            key = '-'.join([str(connection.tenant.pk), template_name])
47
        else:
48
            key = template_name
49
        if template_dirs:
50
            # If template directories were specified, use a hash to differentiate
51
            if connection.tenant:
52
                key = '-'.join([str(connection.tenant.schema_name), template_name, hashlib.sha1(force_bytes('|'.join(template_dirs))).hexdigest()])
53
            else:
54
                key = '-'.join([template_name, hashlib.sha1(force_bytes('|'.join(template_dirs))).hexdigest()])
55

    
56
        if key not in self.template_cache:
57
            template, origin = self.find_template(template_name, template_dirs)
58
            if not hasattr(template, 'render'):
59
                try:
60
                    template = get_template_from_string(template, origin, template_name)
61
                except TemplateDoesNotExist:
62
                    # If compiling the template we found raises TemplateDoesNotExist,
63
                    # back off to returning the source and display name for the template
64
                    # we were asked to load. This allows for correct identification (later)
65
                    # of the actual template that does not exist.
66
                    return template, origin
67
            self.template_cache[key] = template
68
        return self.template_cache[key], None
69

    
70
    def reset(self):
71
        "Empty the template cache."
72
        self.template_cache.clear()
73

    
74
class FilesystemLoader(BaseLoader):
75
    is_usable = True
76

    
77
    def get_template_sources(self, template_name, template_dirs=None):
78
        """
79
        Returns the absolute paths to "template_name", when appended to each
80
        directory in "template_dirs". Any paths that don't lie inside one of the
81
        template dirs are excluded from the result set, for security reasons.
82
        """
83
        if not connection.tenant:
84
            return
85
        if not template_dirs:
86
            try:
87
                template_dirs = settings.TENANT_TEMPLATE_DIRS
88
            except AttributeError:
89
                raise ImproperlyConfigured('To use %s.%s you must define the TENANT_TEMPLATE_DIRS' % (__name__, FilesystemLoader.__name__))
90
        for template_dir in template_dirs:
91
            try:
92
                yield safe_join(template_dir, connection.tenant.domain_url, 'templates', template_name)
93
            except UnicodeDecodeError:
94
                # The template dir name was a bytestring that wasn't valid UTF-8.
95
                raise
96
            except ValueError:
97
                # The joined path was located outside of this particular
98
                # template_dir (it might be inside another one, so this isn't
99
                # fatal).
100
                pass
101

    
102
    def load_template_source(self, template_name, template_dirs=None):
103
        tried = []
104
        for filepath in self.get_template_sources(template_name, template_dirs):
105
            try:
106
                with open(filepath, 'rb') as fp:
107
                    return (fp.read().decode(settings.FILE_CHARSET), filepath)
108
            except IOError:
109
                tried.append(filepath)
110
        if tried:
111
            error_msg = "Tried %s" % tried
112
        else:
113
            error_msg = "Your TENANT_TEMPLATE_DIRS setting is empty. Change it to point to at least one template directory."
114
        raise TemplateDoesNotExist(error_msg)
115
    load_template_source.is_usable = True
(6-6/8)