Project

General

Profile

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

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

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
import tenant_schemas.utils
16

    
17
from . import app_settings
18

    
19
class CachedLoader(BaseLoader):
20
    is_usable = True
21

    
22
    def __init__(self, loaders):
23
        self.template_cache = {}
24
        self._loaders = loaders
25
        self._cached_loaders = []
26

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

    
39
    def find_template(self, name, dirs=None):
40
        for loader in self.loaders:
41
            try:
42
                template, display_name = loader(name, dirs)
43
                return (template, make_origin(display_name, loader, name, dirs))
44
            except TemplateDoesNotExist:
45
                pass
46
        raise TemplateDoesNotExist(name)
47

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

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

    
74
    def reset(self):
75
        "Empty the template cache."
76
        self.template_cache.clear()
77

    
78
class FilesystemLoader(BaseLoader):
79
    is_usable = True
80

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

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