Project

General

Profile

0004-Add-middleware-to-load-settings-from-files-based-on-.patch

Benjamin Dauvergne, 02 September 2014 03:35 PM

Download (5.5 KB)

View differences:

Subject: [PATCH 04/11] Add middleware to load settings from files based on
 the tenant

* Loaded settings are cached based on the mtime of the setting file
* You can load plain python files using PythonSettingsMiddleware, files
  are loaded from <settings.TENANT_BASE>/<domain_url>/settings.py
* You can load JSON files using JSONSettingsMiddleware, setttings are loaded
  from <settings.TENANT_BASE>/<domain_url>/settings.json
 entrouvert/djommon/multitenant/middleware.py |  105 ++++++++++++++++++++++++++
 1 file changed, 105 insertions(+)
entrouvert/djommon/multitenant/middleware.py
1
import os
2
import json
3
import glob
4
import time
5
import hashlib
6

  
1 7
from django.conf import settings, UserSettingsHolder
2 8

  
3 9
from tenant_schemas.middleware import TenantMiddleware
......
18 24
    def process_response(self, request, response):
19 25
        settings._wrapped = self.wrapped
20 26
        return response
27

  
28
class DictAdapter(dict):
29
    def __init__(self, wrapped):
30
        self.wrapped = wrapped
31

  
32
    def __setitem__(self, key, value):
33
        setattr(self.wrapped, key, value)
34

  
35
    def __getitem__(self, key):
36
        try:
37
            return getattr(self.wrapped, key)
38
        except AttributeError:
39
            raise KeyError
40

  
41
class TenantSettingBaseMiddleware(object):
42
    '''Base middleware classe for loading settings based on tenants
43

  
44
       Child classes MUST override the load_tenant_settings() method.
45
    '''
46
    def __init__(self, *args, **kwargs):
47
        self.tenants_settings = {}
48

  
49
    def get_tenant_settings(self, wrapped, tenant):
50
        '''Get last loaded settings for tenant, try to update it by loading
51
           settings again is last loading time is less recent thant settings data
52
           store. Compare with last modification time is done in the
53
           load_tenant_settings() method.
54
        '''
55
        tenant_settings, last_time = self.tenants_settings.get(tenant.schema_name, (None,None))
56
        if tenant_settings is None:
57
            tenant_settings = UserSettingsHolder(wrapped)
58
        tenant_settings, last_time = self.load_tenant_settings(wrapped, tenant, tenant_settings, last_time)
59
        self.tenants_settings[tenant.schema_name] = tenant_settings, last_time
60
        return tenant_settings
61

  
62
    def load_tenant_settings(self, wrapped, tenant, tenant_settings, last_time):
63
        '''Load tenant settings into tenant_settings object, eventually skip if
64
           last_time is more recent than last update time for settings and return
65
           the new value for tenant_settings and last_time'''
66
        raise NotImplemented
67

  
68
    def process_request(self, request):
69
        if not hasattr(request, '_old_settings_wrapped'):
70
            request._old_settings_wrapped = []
71
        request._old_settings_wrapped.append(settings._wrapped)
72
        settings._wrapped = self.get_tenant_settings(settings._wrapped, request.tenant)
73

  
74
    def process_response(self, request, response):
75
        if hasattr(request, '_old_settings_wrapped') and request._old_settings_wrapped:
76
            settings._wrapped = request._old_settings_wrapped.pop()
77
        return response
78

  
79
class PythonSettingsMiddleware(TenantSettingBaseMiddleware):
80
    '''Load settings from a file whose path is given by:
81

  
82
            os.path.join(settings.TENANT_BASE % schema_name, 'settings.py')
83

  
84
       The file is executed in the same context as the classic settings file
85
       using execfile.
86
    '''
87
    def load_tenant_settings(self, wrapped, tenant, tenant_settings, last_time):
88
        path = os.path.join(settings.TENANT_BASE, tenant.schema_name, 'settings.py')
89
        try:
90
            st_mtime = os.stat(path).st_mtime
91
            if not last_time or st_mtime >= last_time:
92
                tenant_settings = UserSettingsHolder(wrapped)
93
                execfile(path, DictAdapter(tenant_settings))
94
                return tenant_settings, st_mtime
95
        except OSError:
96
            pass
97
        return tenant_settings, last_time
98

  
99
class JSONSettingsMiddleware(TenantSettingBaseMiddleware):
100
    '''Load settings from a file whose path is given by:
101

  
102
            os.path.join(settings.TENANT_BASE % schema_name, 'settings.json')
103

  
104
       The file is executed in the same context as the classic settings file
105
       using execfile.
106
    '''
107
    def load_tenant_settings(self, wrapped, tenant, tenant_settings, last_time):
108
        path = os.path.join(settings.TENANT_BASE, tenant.schema_name)
109
        while True:
110
            l = glob.glob(os.path.join(path, '*.json'))
111
            if not l:
112
                h = None
113
            else:
114
                h = ''.join(map(str, (os.stat(p).st_mtime for p in l)))
115
                h += '\0'.join(l)
116
                h = hashlib.md5(h).digest
117
            if h == last_time:
118
                break
119
            last_time = h
120
            tenant_settings = UserSettingsHolder(wrapped)
121
            for p in sorted(glob.glob(os.path.join(path, '*.json'))):
122
                json_settings = json.load(file(p))
123
                for key in json_settings:
124
                    setattr(tenant_settings, key, json_settings[key])
125
        return tenant_settings, last_time
21
-