class DatabaseWrapper(WRAPPED_BACKEND.DatabaseWrapper): def __init__(self, *args, **kwargs): super(DatabaseWrapper, self).__init__(*args, **kwargs) self.threadlocal = MultiTenantThreadlocal() def get_threadlocal(self): return self.threadlocal def _cursor(self): """Supplies a cursor, executing the `USE` statement if required. Ideally we'd override a get_new_connection DatabaseWrapper function, but _cursor() is as close as it gets. """ cursor = super(DatabaseWrapper, self)._cursor() dbname = self.threadlocal.get_dbname() if not dbname: raise ImproperlyConfigured('dbname not set at cursor create time') # Cache the applied dbname as "mt_dbname" on the connection, avoiding # an extra execute() if already set. Importantly, we assume no other # code in the app is executing `USE`. connection = cursor.cursor.connection connection_dbname = getattr(connection, 'mt_dbname', None) if connection_dbname != dbname: start_time = time.time() cursor.execute('USE `%s`;' % dbname) time_ms = int((time.time() - start_time) * 1000) LOGGER.debug('Applied dbname `%s` in %s ms' % (dbname, time_ms)) connection.mt_dbname = dbname return cursor
class DatabaseWrapper(WRAPPED_BACKEND.DatabaseWrapper): def __init__(self, *args, **kwargs): super(DatabaseWrapper, self).__init__(*args, **kwargs) self.threadlocal = MultiTenantThreadlocal() def get_threadlocal(self): return self.threadlocal def create_cursor(self, name=None): cursor = self.connection.cursor() return CursorWrapper(cursor) def _cursor(self): """Supplies a cursor, executing the `USE` statement if required. Ideally we'd override a get_new_connection DatabaseWrapper function, but _cursor() is as close as it gets. """ cursor = super(DatabaseWrapper, self)._cursor() db_name = self.threadlocal.get_db_name() if not db_name: # Django loads the settings after it tries to connect to mysql, when # running management commands If that's the case, update database # name manually update_database_from_env( super(DatabaseWrapper, self).get_connection_params() ) db_name = self.threadlocal.get_db_name() if not db_name: raise ImproperlyConfigured("db_name not set at cursor create time") # Cache the applied db_name as "mt_db_name" on the connection, avoiding # an extra execute() if already set. Importantly, we assume no other # code in the app is executing `USE`. connection = cursor.cursor.connection connection_db_name = getattr(connection, "mt_db_name", None) if connection_db_name != db_name: start_time = time.time() cursor.execute("USE `%s`;" % db_name) time_ms = int((time.time() - start_time) * 1000) LOGGER.debug("Applied db_name `%s` in %s ms", db_name, time_ms) connection.mt_db_name = db_name return cursor
class DatabaseWrapper(WRAPPED_BACKEND.DatabaseWrapper): def __init__(self, *args, **kwargs): super(DatabaseWrapper, self).__init__(*args, **kwargs) self.threadlocal = MultiTenantThreadlocal() self.search_path_set = False def close(self): self.search_path_set = False super(DatabaseWrapper, self).close() def rollback(self): super(DatabaseWrapper, self).rollback() # Django's rollback clears the search path so we have to set it again the next time. self.search_path_set = False def get_threadlocal(self): return self.threadlocal def _cursor(self, name=None): """Supplies a cursor, selecting the schema if required. Ideally we'd override a get_new_connection DatabaseWrapper function, but _cursor() is as close as it gets. """ if name: cursor = super(DatabaseWrapper, self)._cursor(name=name) else: cursor = super(DatabaseWrapper, self)._cursor() tenant_name = self.threadlocal.get_tenant_name() if not tenant_name: raise ImproperlyConfigured( 'Tenant name not set at cursor create time.') # Cache the applied search_path. Importantly, we assume no other # code in the app is executing `SET search_path`. if not self.search_path_set: # Named cursor can only be used once cursor_for_search_path = self.connection.cursor( ) if name else cursor # Nothing prevent tenant_name to be 'foo, public' to provide sharing of tables # (eg. to provide a common table of users). cursor_for_search_path.execute('SET search_path TO %s' % tenant_name) if name: cursor_for_search_path.close() self.search_path_set = True return cursor
class DatabaseWrapper(WRAPPED_BACKEND.DatabaseWrapper): def __init__(self, *args, **kwargs): super(DatabaseWrapper, self).__init__(*args, **kwargs) self.threadlocal = MultiTenantThreadlocal() self.search_path_set = False def close(self): self.search_path_set = False super(DatabaseWrapper, self).close() def rollback(self): super(DatabaseWrapper, self).rollback() # Django's rollback clears the search path so we have to set it again the next time. self.search_path_set = False def get_threadlocal(self): return self.threadlocal def _cursor(self, name=None): """Supplies a cursor, selecting the schema if required. Ideally we'd override a get_new_connection DatabaseWrapper function, but _cursor() is as close as it gets. """ if name: cursor = super(DatabaseWrapper, self)._cursor(name=name) else: cursor = super(DatabaseWrapper, self)._cursor() tenant_name = self.threadlocal.get_tenant_name() if not tenant_name: raise ImproperlyConfigured('Tenant name not set at cursor create time.') # Cache the applied search_path. Importantly, we assume no other # code in the app is executing `SET search_path`. if not self.search_path_set: # Named cursor can only be used once cursor_for_search_path = self.connection.cursor() if name else cursor # Nothing prevent tenant_name to be 'foo, public' to provide sharing of tables # (eg. to provide a common table of users). cursor_for_search_path.execute('SET search_path TO %s' % tenant_name) if name: cursor_for_search_path.close() self.search_path_set = True return cursor
def __init__(self, *args, **kwargs): super(DatabaseWrapper, self).__init__(*args, **kwargs) self.threadlocal = MultiTenantThreadlocal()
def __init__(self, *args, **kwargs): super(DatabaseWrapper, self).__init__(*args, **kwargs) self.threadlocal = MultiTenantThreadlocal() if not self.threadlocal.get_dbname(): self.threadlocal.set_dbname(self.settings_dict['NAME'])
try: # Django versions >= 1.9 from django.utils.module_loading import import_module except ImportError: # Django versions < 1.9 from django.utils.importlib import import_module from db_multitenant.mapper import TenantMapper _CACHED_MAPPER = None import os from django.db import connection from db_multitenant.threadlocal import MultiTenantThreadlocal my_local_global = MultiTenantThreadlocal() def get_mode(): return getattr(settings, 'MULTI_TENANT_MODE', "SCHEMA") def get_threadlocal(): if get_mode() == "SCHEMA": return connection.get_threadlocal() else: # mode == "DATABASE": return my_local_global def get_default_tenant_name(): return getattr(settings, 'DEFAULT_TENANT_NAME', "devnull") def update_database_from_env(db_dict): from django.db import connection