From ec0613c1a8397860277b4c5f17c6823b5c6ad981 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Tue, 19 Aug 2014 11:09:26 +0200 Subject: [PATCH] Add middleware to load settings from a JSON file based on the tenant * Loaded settings are cached based on the mtime of the setting file * JSON file path is //settings.json refs #5106 --- entrouvert/djommon/multitenant/middleware.py | 108 +++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/entrouvert/djommon/multitenant/middleware.py b/entrouvert/djommon/multitenant/middleware.py index 988ad92..468ed7e 100644 --- a/entrouvert/djommon/multitenant/middleware.py +++ b/entrouvert/djommon/multitenant/middleware.py @@ -1,3 +1,6 @@ +import os +import json + from django.conf import settings, UserSettingsHolder from tenant_schemas.middleware import TenantMiddleware @@ -18,3 +21,108 @@ class EOTenantMiddleware(TenantMiddleware): def process_response(self, request, response): settings._wrapped = self.wrapped return response + +class TenantSettingBaseMiddleware(object): + '''Base middleware classe for loading settings based on tenants + + Child classes MUST override the load_tenant_settings() method. + ''' + def __init__(self, *args, **kwargs): + self.tenants_settings = {} + + def get_tenant_settings(self, wrapped, tenant): + '''Get last loaded settings for tenant, try to update it by loading + settings again is last loading time is less recent thant settings data + store. Compare with last modification time is done in the + load_tenant_settings() method. + ''' + tenant_settings, last_time = self.tenants_settings.get(tenant.schema_name, (None,None)) + if tenant_settings is None: + tenant_settings = UserSettingsHolder(wrapped) + tenant_settings, last_time = self.load_tenant_settings(wrapped, tenant, tenant_settings, last_time) + self.tenants_settings[tenant.schema_name] = tenant_settings, last_time + return tenant_settings + + def load_tenant_settings(self, wrapped, tenant, tenant_settings, last_time): + '''Load tenant settings into tenant_settings object, eventually skip if + last_time is more recent than last update time for settings and return + the new value for tenant_settings and last_time''' + raise NotImplemented + + def process_request(self, request): + if not hasattr(request, '_old_settings_wrapped'): + request._old_settings_wrapped = [] + request._old_settings_wrapped.append(settings._wrapped) + settings._wrapped = self.get_tenant_settings(settings._wrapped, request.tenant) + + def process_response(self, request, response): + if hasattr(request, '_old_settings_wrapped') and request._old_settings_wrapped: + settings._wrapped = request._old_settings_wrapped.pop() + return response + + +class FileBasedTenantSettingBaseMiddleware(TenantSettingBaseMiddleware): + FILENAME = None + + def load_tenant_settings(self, wrapped, tenant, tenant_settings, last_time): + path = os.path.join(settings.TENANT_BASE, tenant.schema_name, self.FILENAME) + try: + new_time = os.stat(path).st_mtime + except OSError: + # file was removed + if not last_time is None: + return UserSettingsHolder(wrapped), None + else: + if last_time is None or new_time >= last_time: + # file is new + tenant_settings = UserSettingsHolder(wrapped) + self.load_file(tenant_settings, path) + return tenant_settings, new_time + # nothing has changed + return tenant_settings, last_time + + +class JSONSettingsMiddleware(FileBasedTenantSettingBaseMiddleware): + '''Load settings from a JSON file whose path is given by: + + os.path.join(settings.TENANT_BASE % schema_name, 'settings.json') + + The JSON file must be a dictionnary whose key/value will override + current settings. + ''' + FILENAME = 'settings.json' + + def load_file(sef, tenant_settings, path): + with file(path) as f: + json_settings = json.load(f) + for key in json_settings: + setattr(tenant_settings, key, json_settings[key]) + + +class DictAdapter(dict): + '''Give dict interface to plain objects''' + def __init__(self, wrapped): + self.wrapped = wrapped + + def __setitem__(self, key, value): + setattr(self.wrapped, key, value) + + def __getitem__(self, key): + try: + return getattr(self.wrapped, key) + except AttributeError: + raise KeyError + + +class PythonSettingsMiddleware(JSONSettingsMiddleware): + '''Load settings from a file whose path is given by: + + os.path.join(settings.TENANT_BASE % schema_name, 'settings.py') + + The file is executed in the same context as the classic settings file + using execfile. + ''' + FILENAME = 'settings.py' + + def load_file(self, tenant_settings, path): + execfile(path, DictAdapter(tenant_settings))