hobo/hobo/multitenant/settings.py

105 lines
3.9 KiB
Python

# hobo - portal to configure and deploy applications
# Copyright (C) 2015-2016 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import threading
import time
from importlib import import_module
from django.conf import UserSettingsHolder
from django.db import connection
def import_class(klass):
module, attr = klass.rsplit('.', 1)
return getattr(import_module(module), attr)
class TenantSettingsWrapper:
local = threading.local()
def __init__(self, default_settings):
self.__dict__['tenants_settings'] = {}
self.__dict__['default_settings'] = default_settings
def clear_tenants_settings(self):
self.__dict__['tenants_settings'] = {}
@property
def loaders(self):
loaders = getattr(self.default_settings, 'TENANT_SETTINGS_LOADERS', [])
for loader in loaders:
loader_class = import_class(loader)
yield loader_class()
def load_tenant_settings(self, tenant, tenant_settings, last_time):
"""Load tenant settings from loaders into tenant_settings object, only
if any of the loaders say it is more recent than last update time"""
update_time = time.time()
if last_time and update_time - last_time < 3:
return tenant_settings, last_time
new = False
for loader in self.loaders:
new_time = loader.get_new_time(tenant)
if (
(not new_time and last_time)
or (new_time and not last_time)
or (new_time and new_time > last_time)
):
new = True
break
if new:
tenant_settings = UserSettingsHolder(self.default_settings)
# something is new, call all loaders
for loader in self.loaders:
loader.update_settings(tenant_settings, tenant)
return tenant_settings, update_time
return tenant_settings, last_time
def get_tenant_settings(self, 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.domain_url, (None, None))
if tenant_settings is None:
tenant_settings = UserSettingsHolder(self.default_settings)
tenant_settings, last_time = self.load_tenant_settings(tenant, tenant_settings, last_time)
self.tenants_settings[tenant.domain_url] = tenant_settings, last_time
return tenant_settings
def get_wrapped(self):
if getattr(self.local, 'in_get_wrapped', False):
return self.default_settings
try:
self.local.in_get_wrapped = True
tenant = connection.get_tenant()
if not hasattr(tenant, 'domain_url'):
return self.default_settings
return self.get_tenant_settings(tenant)
finally:
self.local.in_get_wrapped = False
def __getattr__(self, name):
return getattr(self.get_wrapped(), name)
def __setattr__(self, name, value):
setattr(self.get_wrapped(), name, value)
def __delattr__(self, name):
delattr(self.get_wrapped(), name)