From 43d6b99c0f1273d62706c733a3a5e57ee30b712d Mon Sep 17 00:00:00 2001 From: Thomas NOEL Date: Thu, 12 Mar 2015 18:55:15 +0100 Subject: [PATCH] remove multitenant, now in hobo (#6491) --- entrouvert/djommon/multitenant/README | 99 ---------- entrouvert/djommon/multitenant/__init__.py | 0 .../multitenant/management/__init__.py | 0 .../management/commands/__init__.py | 171 ----------------- .../management/commands/create_schemas.py | 19 -- .../management/commands/create_tenant.py | 36 ---- .../management/commands/createsuperuser.py | 11 -- .../multitenant/management/commands/deploy.py | 30 --- .../management/commands/legacy/__init__.py | 0 .../commands/legacy/migrate_schemas.py | 91 --------- .../management/commands/list_tenants.py | 14 -- .../management/commands/migrate.py | 27 --- .../management/commands/migrate_schemas.py | 68 ------- .../commands/safemigrate_schemas.py | 105 ---------- .../management/commands/sync_schemas.py | 87 --------- .../multitenant/management/commands/syncdb.py | 26 --- .../management/commands/tenant_command.py | 40 ---- entrouvert/djommon/multitenant/middleware.py | 180 ------------------ entrouvert/djommon/multitenant/models.py | 11 -- entrouvert/djommon/multitenant/storage.py | 24 --- .../djommon/multitenant/template_loader.py | 115 ----------- entrouvert/djommon/multitenant/tests.py | 128 ------------- entrouvert/djommon/multitenant/views.py | 1 - 23 files changed, 1283 deletions(-) delete mode 100644 entrouvert/djommon/multitenant/README delete mode 100644 entrouvert/djommon/multitenant/__init__.py delete mode 100644 entrouvert/djommon/multitenant/management/__init__.py delete mode 100644 entrouvert/djommon/multitenant/management/commands/__init__.py delete mode 100644 entrouvert/djommon/multitenant/management/commands/create_schemas.py delete mode 100644 entrouvert/djommon/multitenant/management/commands/create_tenant.py delete mode 100644 entrouvert/djommon/multitenant/management/commands/createsuperuser.py delete mode 100644 entrouvert/djommon/multitenant/management/commands/deploy.py delete mode 100644 entrouvert/djommon/multitenant/management/commands/legacy/__init__.py delete mode 100644 entrouvert/djommon/multitenant/management/commands/legacy/migrate_schemas.py delete mode 100644 entrouvert/djommon/multitenant/management/commands/list_tenants.py delete mode 100644 entrouvert/djommon/multitenant/management/commands/migrate.py delete mode 100644 entrouvert/djommon/multitenant/management/commands/migrate_schemas.py delete mode 100644 entrouvert/djommon/multitenant/management/commands/safemigrate_schemas.py delete mode 100644 entrouvert/djommon/multitenant/management/commands/sync_schemas.py delete mode 100644 entrouvert/djommon/multitenant/management/commands/syncdb.py delete mode 100644 entrouvert/djommon/multitenant/management/commands/tenant_command.py delete mode 100644 entrouvert/djommon/multitenant/middleware.py delete mode 100644 entrouvert/djommon/multitenant/models.py delete mode 100644 entrouvert/djommon/multitenant/storage.py delete mode 100644 entrouvert/djommon/multitenant/template_loader.py delete mode 100644 entrouvert/djommon/multitenant/tests.py delete mode 100644 entrouvert/djommon/multitenant/views.py diff --git a/entrouvert/djommon/multitenant/README b/entrouvert/djommon/multitenant/README deleted file mode 100644 index fb2b176..0000000 --- a/entrouvert/djommon/multitenant/README +++ /dev/null @@ -1,99 +0,0 @@ -Multitenant ------------ - -An application for making a Django application multitenant for Entr'ouvert -customers. - -Based on https://django-tenant-schemas.readthedocs.org/ - -It is developed, tested and supported on Django 1.7, but it should work with -Django 1.6 + south. - - -Install -------- - -See also : https://django-tenant-schemas.readthedocs.org/ - -Set the tenant model: - - TENANT_MODEL = 'multitenant.Tenant' - -Where are tenants: - - TENANT_BASE = '/var/lib//tenants' - -Add the middlewares for multitenant, they must be first: - - MIDDLEWARE_CLASSES = ( - 'entrouvert.djommon.multitenant.middleware.TenantMiddleware', - 'entrouvert.djommon.multitenant.middleware.JSONSettingsMiddleware', - 'entrouvert.djommon.multitenant.middleware.PythonSettingsMiddleware', - ) + MIDDLEWARE_CLASSES - -Define the shared applications: - - SHARED_APPS = ( - 'tenant_schemas', - 'entrouvert.djommon.multitenant', - # those are needed for the public apps to work - # add also any application needed by the public app - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - ) - - TENANT_APPS = INSTALLED_APPS - - INSTALLED_APPS = ('entrouvert.djommon.multitenant', - 'tenant_schemas') + INSTALLED_APPS - - # or, with Django 1.6 or older: - # INSTALLED_APPS += ('tenant_schemas', 'entrouvert.djommon.multitenant') - -Use multitenant database engine: - - DATABASES = { - 'default': { - 'ENGINE': 'tenant_schemas.postgresql_backend', - 'NAME': '', - }, - } - DATABASE_ROUTERS = ( - 'tenant_schemas.routers.TenantSyncRouter', - ) - - # With Django 1.6 or older, use multitenant south adapter: - # SOUTH_DATABASE_ADAPTERS = {'default': 'south.db.postgresql_psycopg2'} - -Add the multitenant filesystem template loader and configure where the -multitenant templates are located: - - TEMPLATE_LOADERS = ( - 'entrouvert.djommon.multitenant.template_loader.FilesystemLoader', - ) + TEMPLATE_LOADERS - TENANT_TEMPLATE_DIRS = (TENANT_BASE,) - - TEMPLATE_CONTEXT_PROCESSORS = ( - 'django.core.context_processors.request', - ) + TEMPLATE_CONTEXT_PROCESSORS - - -Usage ------ - -Create a tenant: - manage.py create_tenant www.example.net -Migration of all tenants: - manage.py migrate_schemas -Add a super user to tenant: - manage.py tenant_command createsuperuser --domain www.example.net - -Tenants are created in TENANT_BASE directory, for example : - /var/lib/project/tenants/www.example.net/ - templates/ <-- override project templates - static/ <-- to be handled by HTTP server - media/ -Each tenant is a PostgreSQL schema, named www_example_net diff --git a/entrouvert/djommon/multitenant/__init__.py b/entrouvert/djommon/multitenant/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/entrouvert/djommon/multitenant/management/__init__.py b/entrouvert/djommon/multitenant/management/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/entrouvert/djommon/multitenant/management/commands/__init__.py b/entrouvert/djommon/multitenant/management/commands/__init__.py deleted file mode 100644 index 601f3fd..0000000 --- a/entrouvert/djommon/multitenant/management/commands/__init__.py +++ /dev/null @@ -1,171 +0,0 @@ -# this file derive from django-tenant-schemas -# Author: Bernardo Pires Carneiro -# Email: carneiro.be@gmail.com -# License: MIT license -# Home-page: http://github.com/bcarneiro/django-tenant-schemas -from optparse import make_option -from django.conf import settings -from django.core.management import call_command, get_commands, load_command_class -from django.core.management.base import BaseCommand, CommandError -from django.db import connection -try: - from django.utils.six.moves import input -except ImportError: - input = raw_input -from tenant_schemas.utils import get_public_schema_name -from entrouvert.djommon.multitenant.middleware import TenantMiddleware - - -class BaseTenantCommand(BaseCommand): - """ - Generic command class useful for iterating any existing command - over all schemata. The actual command name is expected in the - class variable COMMAND_NAME of the subclass. - """ - def __new__(cls, *args, **kwargs): - """ - Sets option_list and help dynamically. - """ - obj = super(BaseTenantCommand, cls).__new__(cls, *args, **kwargs) - - app_name = get_commands()[obj.COMMAND_NAME] - if isinstance(app_name, BaseCommand): - # If the command is already loaded, use it directly. - cmdclass = app_name - else: - cmdclass = load_command_class(app_name, obj.COMMAND_NAME) - - # inherit the options from the original command - obj.option_list = cmdclass.option_list - obj.option_list += ( - make_option("-d", "--domain", dest="domain"), - ) - obj.option_list += ( - make_option("-p", "--skip-public", dest="skip_public", action="store_true", default=False), - ) - - # prepend the command's original help with the info about schemata iteration - obj.help = "Calls %s for all registered schemata. You can use regular %s options. "\ - "Original help for %s: %s" % (obj.COMMAND_NAME, obj.COMMAND_NAME, obj.COMMAND_NAME, - getattr(cmdclass, 'help', 'none')) - return obj - - def execute_command(self, tenant, command_name, *args, **options): - verbosity = int(options.get('verbosity')) - - if verbosity >= 1: - print() - print(self.style.NOTICE("=== Switching to schema '") \ - + self.style.SQL_TABLE(tenant.schema_name)\ - + self.style.NOTICE("' then calling %s:" % command_name)) - - connection.set_tenant(tenant) - - # call the original command with the args it knows - call_command(command_name, *args, **options) - - def handle(self, *args, **options): - """ - Iterates a command over all registered schemata. - """ - if options['domain']: - # only run on a particular schema - connection.set_schema_to_public() - self.execute_command(TenantMiddleware.get_tenant_by_hostname(options['domain']), self.COMMAND_NAME, *args, **options) - else: - for tenant in TenantMiddleware.get_tenants(): - if not(options['skip_public'] and tenant.schema_name == get_public_schema_name()): - self.execute_command(tenant, self.COMMAND_NAME, *args, **options) - - -class InteractiveTenantOption(object): - def __init__(self, *args, **kwargs): - super(InteractiveTenantOption, self).__init__(*args, **kwargs) - self.option_list += ( - make_option("-d", "--domain", dest="domain", help="specify tenant domain"), - ) - - def get_tenant_from_options_or_interactive(self, **options): - all_tenants = list(TenantMiddleware.get_tenants()) - - if not all_tenants: - raise CommandError("""There are no tenants in the system. -To learn how create a tenant, see: -https://django-tenant-schemas.readthedocs.org/en/latest/use.html#creating-a-tenant""") - - if options.get('domain'): - domain = options['domain'] - else: - while True: - domain = input("Enter Tenant Domain ('?' to list): ") - if domain == '?': - print('\n'.join(["%s (schema %s)" % (t.domain_url, t.schema_name) for t in all_tenants])) - else: - break - - if domain not in [t.domain_url for t in all_tenants]: - raise CommandError("Invalid tenant, '%s'" % (domain,)) - - return TenantMiddleware.get_tenant_by_hostname(domain) - - -class TenantWrappedCommand(InteractiveTenantOption, BaseCommand): - """ - Generic command class useful for running any existing command - on a particular tenant. The actual command name is expected in the - class variable COMMAND_NAME of the subclass. - """ - def __new__(cls, *args, **kwargs): - obj = super(TenantWrappedCommand, cls).__new__(cls, *args, **kwargs) - obj.command_instance = obj.COMMAND() - obj.option_list = obj.command_instance.option_list - return obj - - def handle(self, *args, **options): - tenant = self.get_tenant_from_options_or_interactive(**options) - connection.set_tenant(tenant) - - self.command_instance.execute(*args, **options) - - -class SyncCommon(BaseCommand): - option_list = ( - make_option('--tenant', action='store_true', dest='tenant', default=False, - help='Tells Django to populate only tenant applications.'), - make_option('--shared', action='store_true', dest='shared', default=False, - help='Tells Django to populate only shared applications.'), - make_option("-d", "--domain", dest="domain"), - ) - - def handle(self, *args, **options): - self.sync_tenant = options.get('tenant') - self.sync_public = options.get('shared') - self.domain = options.get('domain') - self.installed_apps = settings.INSTALLED_APPS - self.args = args - self.options = options - - if self.domain: - self.schema_name = TenantMiddleware.hostname2schema(domain) - else: - self.schema_name = options.get('schema_name') - - if self.schema_name: - if self.sync_public: - raise CommandError("domain should only be used with the --tenant switch.") - elif self.schema_name == get_public_schema_name(): - self.sync_public = True - else: - self.sync_tenant = True - elif not self.sync_public and not self.sync_tenant: - # no options set, sync both - self.sync_tenant = True - self.sync_public = True - - if hasattr(settings, 'TENANT_APPS'): - self.tenant_apps = settings.TENANT_APPS - if hasattr(settings, 'SHARED_APPS'): - self.shared_apps = settings.SHARED_APPS - - def _notice(self, output): - self.stdout.write(self.style.NOTICE(output)) diff --git a/entrouvert/djommon/multitenant/management/commands/create_schemas.py b/entrouvert/djommon/multitenant/management/commands/create_schemas.py deleted file mode 100644 index a92e783..0000000 --- a/entrouvert/djommon/multitenant/management/commands/create_schemas.py +++ /dev/null @@ -1,19 +0,0 @@ -from django.core.management.base import BaseCommand -from entrouvert.djommon.multitenant.middleware import TenantMiddleware -from django.db import connection - -class Command(BaseCommand): - help = "Create schemas for all declared tenants" - - def handle(self, *args, **options): - verbosity = int(options.get('verbosity')) - - connection.set_schema_to_public() - all_tenants = TenantMiddleware.get_tenants() - for tenant in all_tenants: - if verbosity >= 1: - print - print self.style.NOTICE("=== Creating schema ") \ - + self.style.SQL_TABLE(tenant.schema_name) - - tenant.create_schema(check_if_exists=True) diff --git a/entrouvert/djommon/multitenant/management/commands/create_tenant.py b/entrouvert/djommon/multitenant/management/commands/create_tenant.py deleted file mode 100644 index ed9dded..0000000 --- a/entrouvert/djommon/multitenant/management/commands/create_tenant.py +++ /dev/null @@ -1,36 +0,0 @@ -import os - -from django.db import connection -from django.core.management.base import CommandError, BaseCommand - -from entrouvert.djommon.multitenant.middleware import TenantMiddleware - -class Command(BaseCommand): - help = "Create tenant(s) by hostname(s)" - - def handle(self, *args, **options): - verbosity = int(options.get('verbosity')) - if not args: - raise CommandError("you must give at least one tenant hostname") - - for hostname in args: - try: - tenant_base = TenantMiddleware.base() - except AttributeError: - raise CommandError("you must configure TENANT_BASE in your settings") - if not tenant_base: - raise CommandError("you must set a value to TENANT_BASE in your settings") - tenant_dir = os.path.join(tenant_base, hostname) - if not os.path.exists(tenant_dir): - os.mkdir(tenant_dir, 0755) - for folder in ('media', 'static', 'templates'): - path = os.path.join(tenant_dir, folder) - if not os.path.exists(path): - os.mkdir(path, 0755) - connection.set_schema_to_public() - tenant = TenantMiddleware.get_tenant_by_hostname(hostname) - if verbosity >= 1: - print - print self.style.NOTICE("=== Creating schema ") \ - + self.style.SQL_TABLE(tenant.schema_name) - tenant.create_schema(check_if_exists=True) diff --git a/entrouvert/djommon/multitenant/management/commands/createsuperuser.py b/entrouvert/djommon/multitenant/management/commands/createsuperuser.py deleted file mode 100644 index 54fc9f3..0000000 --- a/entrouvert/djommon/multitenant/management/commands/createsuperuser.py +++ /dev/null @@ -1,11 +0,0 @@ -# this file derive from django-tenant-schemas -# Author: Bernardo Pires Carneiro -# Email: carneiro.be@gmail.com -# License: MIT license -# Home-page: http://github.com/bcarneiro/django-tenant-schemas -from entrouvert.djommon.multitenant.management.commands import TenantWrappedCommand -from django.contrib.auth.management.commands import createsuperuser - - -class Command(TenantWrappedCommand): - COMMAND = createsuperuser.Command diff --git a/entrouvert/djommon/multitenant/management/commands/deploy.py b/entrouvert/djommon/multitenant/management/commands/deploy.py deleted file mode 100644 index f37d155..0000000 --- a/entrouvert/djommon/multitenant/management/commands/deploy.py +++ /dev/null @@ -1,30 +0,0 @@ -import urllib2 -import json -import sys - -from django.core.management import call_command -from django.core.management.base import BaseCommand, CommandError - -from tenant_schemas.utils import tenant_context -from entrouvert.djommon.multitenant.middleware import TenantMiddleware - -class Command(BaseCommand): - help = 'Deploy a tenant from hobo' - - def handle(self, base_url, **options): - environment = json.load(sys.stdin) - for service in environment['services']: - if service['base_url'] == base_url: - break - else: - raise CommandError('Service %s not found' % base_url) - hostname = urllib2.urlparse.urlsplit(base_url).netloc - - call_command('create_tenant', hostname) - - tenant = TenantMiddleware.get_tenant_by_hostname(hostname) - with tenant_context(tenant): - self.deploy_tenant(environment, service, options) - - def deploy_tenant(self, environment, service, options): - pass diff --git a/entrouvert/djommon/multitenant/management/commands/legacy/__init__.py b/entrouvert/djommon/multitenant/management/commands/legacy/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/entrouvert/djommon/multitenant/management/commands/legacy/migrate_schemas.py b/entrouvert/djommon/multitenant/management/commands/legacy/migrate_schemas.py deleted file mode 100644 index 5dd204a..0000000 --- a/entrouvert/djommon/multitenant/management/commands/legacy/migrate_schemas.py +++ /dev/null @@ -1,91 +0,0 @@ -# this file derive from django-tenant-schemas -# Author: Bernardo Pires Carneiro -# Email: carneiro.be@gmail.com -# License: MIT license -# Home-page: http://github.com/bcarneiro/django-tenant-schemas -from django.conf import settings -from django.db import connection -from south import migration -from south.migration.base import Migrations -from south.management.commands.migrate import Command as MigrateCommand -from entrouvert.djommon.multitenant.middleware import TenantMiddleware -from entrouvert.djommon.multitenant.management.commands import SyncCommon - - -class Command(SyncCommon): - help = "Migrate schemas with South" - option_list = MigrateCommand.option_list + SyncCommon.option_list - - def handle(self, *args, **options): - super(Command, self).handle(*args, **options) - - if self.sync_public: - self.migrate_public_apps() - if self.sync_tenant: - self.migrate_tenant_apps(self.domain) - - def _set_managed_apps(self, included_apps, excluded_apps): - """ while sync_schemas works by setting which apps are managed, on south we set which apps should be ignored """ - ignored_apps = [] - if excluded_apps: - for item in excluded_apps: - if item not in included_apps: - ignored_apps.append(item) - - for app in ignored_apps: - app_label = app.split('.')[-1] - settings.SOUTH_MIGRATION_MODULES[app_label] = 'ignore' - - def _save_south_settings(self): - self._old_south_modules = None - if hasattr(settings, "SOUTH_MIGRATION_MODULES") and settings.SOUTH_MIGRATION_MODULES is not None: - self._old_south_modules = settings.SOUTH_MIGRATION_MODULES.copy() - else: - settings.SOUTH_MIGRATION_MODULES = dict() - - def _restore_south_settings(self): - settings.SOUTH_MIGRATION_MODULES = self._old_south_modules - - def _clear_south_cache(self): - for mig in list(migration.all_migrations()): - delattr(mig._application, "migrations") - Migrations._clear_cache() - - def _migrate_schema(self, tenant): - connection.set_tenant(tenant, include_public=False) - MigrateCommand().execute(*self.args, **self.options) - - def migrate_tenant_apps(self, schema_name=None): - self._save_south_settings() - - apps = self.tenant_apps or self.installed_apps - self._set_managed_apps(included_apps=apps, excluded_apps=self.shared_apps) - - if schema_name: - self._notice("=== Running migrate for schema: %s" % schema_name) - connection.set_schema_to_public() - tenant = TenantMiddleware.get_tenant_by_hostname(schema_name) - self._migrate_schema(tenant) - else: - all_tenants = TenantMiddleware.get_tenants() - if not all_tenants: - self._notice("No tenants found") - - for tenant in all_tenants: - Migrations._dependencies_done = False # very important, the dependencies need to be purged from cache - self._notice("=== Running migrate for schema %s" % tenant.schema_name) - self._migrate_schema(tenant) - - self._restore_south_settings() - - def migrate_public_apps(self): - self._save_south_settings() - - apps = self.shared_apps or self.installed_apps - self._set_managed_apps(included_apps=apps, excluded_apps=self.tenant_apps) - - self._notice("=== Running migrate for schema public") - MigrateCommand().execute(*self.args, **self.options) - - self._clear_south_cache() - self._restore_south_settings() diff --git a/entrouvert/djommon/multitenant/management/commands/list_tenants.py b/entrouvert/djommon/multitenant/management/commands/list_tenants.py deleted file mode 100644 index 708e17c..0000000 --- a/entrouvert/djommon/multitenant/management/commands/list_tenants.py +++ /dev/null @@ -1,14 +0,0 @@ -from django.core.management.base import BaseCommand -from entrouvert.djommon.multitenant.middleware import TenantMiddleware - -class Command(BaseCommand): - requires_model_validation = True - can_import_settings = True - option_list = BaseCommand.option_list - - def handle(self, **options): - all_tenants = TenantMiddleware.get_tenants() - - for tenant in all_tenants: - print("{0} {1}".format(tenant.schema_name, tenant.domain_url)) - diff --git a/entrouvert/djommon/multitenant/management/commands/migrate.py b/entrouvert/djommon/multitenant/management/commands/migrate.py deleted file mode 100644 index a43849c..0000000 --- a/entrouvert/djommon/multitenant/management/commands/migrate.py +++ /dev/null @@ -1,27 +0,0 @@ -# this file derive from django-tenant-schemas -# Author: Bernardo Pires Carneiro -# Email: carneiro.be@gmail.com -# License: MIT license -# Home-page: http://github.com/bcarneiro/django-tenant-schemas -import django -from django.conf import settings -from django.core.management.base import CommandError, BaseCommand - -if django.VERSION < (1, 7, 0): - try: - from south.management.commands.migrate import Command as MigrateCommand - except ImportError: - MigrateCommand = BaseCommand -else: - MigrateCommand = BaseCommand - -class Command(MigrateCommand): - - def handle(self, *args, **options): - database = options.get('database', 'default') - if (settings.DATABASES[database]['ENGINE'] == 'tenant_schemas.postgresql_backend' or - MigrateCommand is BaseCommand): - raise CommandError("migrate has been disabled, for database '{}'. Use migrate_schemas " - "instead. Please read the documentation if you don't know why you " - "shouldn't call migrate directly!".format(database)) - super(Command, self).handle(*args, **options) diff --git a/entrouvert/djommon/multitenant/management/commands/migrate_schemas.py b/entrouvert/djommon/multitenant/management/commands/migrate_schemas.py deleted file mode 100644 index 074dfd2..0000000 --- a/entrouvert/djommon/multitenant/management/commands/migrate_schemas.py +++ /dev/null @@ -1,68 +0,0 @@ -import django -from optparse import NO_DEFAULT - -if django.VERSION >= (1, 7, 0): - from django.core.management.commands.migrate import Command as MigrateCommand - from django.db.migrations.recorder import MigrationRecorder -from django.db import connection -from django.conf import settings - -from tenant_schemas.utils import get_public_schema_name -from entrouvert.djommon.multitenant.middleware import TenantMiddleware, TenantNotFound -from entrouvert.djommon.multitenant.management.commands import SyncCommon - - -class MigrateSchemasCommand(SyncCommon): - help = "Updates database schema. Manages both apps with migrations and those without." - - def run_from_argv(self, argv): - """ - Changes the option_list to use the options from the wrapped command. - Adds schema parameter to specify which schema will be used when - executing the wrapped command. - """ - self.option_list += MigrateCommand.option_list - super(MigrateSchemasCommand, self).run_from_argv(argv) - - def handle(self, *args, **options): - super(MigrateSchemasCommand, self).handle(*args, **options) - self.PUBLIC_SCHEMA_NAME = get_public_schema_name() - - if self.sync_public and not self.schema_name: - self.schema_name = self.PUBLIC_SCHEMA_NAME - - if self.sync_public: - self.run_migrations(self.schema_name, settings.SHARED_APPS) - if self.sync_tenant: - if self.schema_name and self.schema_name != self.PUBLIC_SCHEMA_NAME: - self.run_migrations(self.schema_name, settings.TENANT_APPS) - else: - all_tenants = TenantMiddleware.get_tenants() - for tenant in all_tenants: - self.run_migrations(tenant.schema_name, settings.TENANT_APPS) - - def run_migrations(self, schema_name, included_apps): - self._notice("=== Running migrate for schema %s" % schema_name) - connection.set_schema(schema_name) - command = MigrateCommand() - - defaults = {} - for opt in MigrateCommand.option_list: - if opt.dest in self.options: - defaults[opt.dest] = self.options[opt.dest] - elif opt.default is NO_DEFAULT: - defaults[opt.dest] = None - else: - defaults[opt.dest] = opt.default - - command.execute(*self.args, **defaults) - connection.set_schema_to_public() - - def _notice(self, output): - self.stdout.write(self.style.NOTICE(output)) - - -if django.VERSION >= (1, 7, 0): - Command = MigrateSchemasCommand -else: - from .legacy.migrate_schemas import Command diff --git a/entrouvert/djommon/multitenant/management/commands/safemigrate_schemas.py b/entrouvert/djommon/multitenant/management/commands/safemigrate_schemas.py deleted file mode 100644 index 207441a..0000000 --- a/entrouvert/djommon/multitenant/management/commands/safemigrate_schemas.py +++ /dev/null @@ -1,105 +0,0 @@ -# this file derive from django-tenant-schemas -# Author: Bernardo Pires Carneiro -# Email: carneiro.be@gmail.com -# License: MIT license -# Home-page: http://github.com/bcarneiro/django-tenant-schemas -import django - -if django.VERSION < (1, 7, 0): - from django.conf import settings - from django.db import connection - from south import migration - from south.migration.base import Migrations - from entrouvert.djommon.multitenant.middleware import TenantMiddleware - from entrouvert.djommon.management.commands.safemigrate import Command as SafeMigrateCommand - from entrouvert.djommon.multitenant.management.commands.sync_schemas import Command as MTSyncCommand - from entrouvert.djommon.multitenant.management.commands.migrate_schemas import Command as MTMigrateCommand -from entrouvert.djommon.multitenant.management.commands import SyncCommon - - -class SafeMigrateCommand(SyncCommon): - help = "Safely migrate schemas with South" - option_list = MTMigrateCommand.option_list - - def handle(self, *args, **options): - super(Command, self).handle(*args, **options) - - MTSyncCommand().execute(*args, **options) - connection.set_schema_to_public() - if self.sync_public: - self.fake_public_apps() - if self.sync_tenant: - self.fake_tenant_apps(self.domain) - connection.set_schema_to_public() - MTMigrateCommand().execute(*args, **options) - - def _set_managed_apps(self, included_apps, excluded_apps): - """ while sync_schemas works by setting which apps are managed, on south we set which apps should be ignored """ - ignored_apps = [] - if excluded_apps: - for item in excluded_apps: - if item not in included_apps: - ignored_apps.append(item) - - for app in ignored_apps: - app_label = app.split('.')[-1] - settings.SOUTH_MIGRATION_MODULES[app_label] = 'ignore' - - def _save_south_settings(self): - self._old_south_modules = None - if hasattr(settings, "SOUTH_MIGRATION_MODULES") and settings.SOUTH_MIGRATION_MODULES is not None: - self._old_south_modules = settings.SOUTH_MIGRATION_MODULES.copy() - else: - settings.SOUTH_MIGRATION_MODULES = dict() - - def _restore_south_settings(self): - settings.SOUTH_MIGRATION_MODULES = self._old_south_modules - - def _clear_south_cache(self): - for mig in list(migration.all_migrations()): - delattr(mig._application, "migrations") - Migrations._clear_cache() - - def _fake_schema(self, tenant): - connection.set_tenant(tenant, include_public=False) - SafeMigrateCommand().fake_if_needed() - - def fake_tenant_apps(self, schema_name=None): - self._save_south_settings() - - apps = self.tenant_apps or self.installed_apps - self._set_managed_apps(included_apps=apps, excluded_apps=self.shared_apps) - - if schema_name: - self._notice("=== Running fake_if_needed for schema: %s" % schema_name) - connection.set_schema_to_public() - tenant = TenantMiddleware.get_tenant_by_hostname(schema_name) - self._fake_schema(tenant) - else: - all_tenants = TenantMiddleware.get_tenants() - if not all_tenants: - self._notice("No tenants found") - - for tenant in all_tenants: - Migrations._dependencies_done = False # very important, the dependencies need to be purged from cache - self._notice("=== Running fake_if_needed for schema %s" % tenant.schema_name) - self._fake_schema(tenant) - - self._restore_south_settings() - - def fake_public_apps(self): - self._save_south_settings() - - apps = self.shared_apps or self.installed_apps - self._set_managed_apps(included_apps=apps, excluded_apps=self.tenant_apps) - - self._notice("=== Running fake_if_needed for schema public") - SafeMigrateCommand().fake_if_needed() - - self._clear_south_cache() - self._restore_south_settings() - -if django.VERSION < (1, 7, 0): - Command = SafeMigrateCommand -else: - raise RuntimeError('Django 1.7: please use migrate_schemas') diff --git a/entrouvert/djommon/multitenant/management/commands/sync_schemas.py b/entrouvert/djommon/multitenant/management/commands/sync_schemas.py deleted file mode 100644 index fa4acd9..0000000 --- a/entrouvert/djommon/multitenant/management/commands/sync_schemas.py +++ /dev/null @@ -1,87 +0,0 @@ -# this file derive from django-tenant-schemas -# Author: Bernardo Pires Carneiro -# Email: carneiro.be@gmail.com -# License: MIT license -# Home-page: http://github.com/bcarneiro/django-tenant-schemas -import django -from django.core.management.base import CommandError - -from django.conf import settings -from django.contrib.contenttypes.models import ContentType -from django.db.models import get_apps, get_models -if 'south' in settings.INSTALLED_APPS: - from south.management.commands.syncdb import Command as SyncdbCommand -else: - from django.core.management.commands.syncdb import Command as SyncdbCommand -from django.db import connection -from entrouvert.djommon.multitenant.middleware import TenantMiddleware -from entrouvert.djommon.multitenant.management.commands import SyncCommon - - -class Command(SyncCommon): - help = "Sync schemas based on TENANT_APPS and SHARED_APPS settings" - option_list = SyncdbCommand.option_list + SyncCommon.option_list - - def handle(self, *args, **options): - if django.VERSION >= (1, 7, 0): - raise CommandError('This command is only meant to be used for 1.6' - ' and older version of django. For 1.7, use' - ' `migrate_schemas` instead.') - super(Command, self).handle(*args, **options) - - if "south" in settings.INSTALLED_APPS: - self.options["migrate"] = False - - # save original settings - for model in get_models(include_auto_created=True): - setattr(model._meta, 'was_managed', model._meta.managed) - - ContentType.objects.clear_cache() - - if self.sync_public: - self.sync_public_apps() - if self.sync_tenant: - self.sync_tenant_apps(self.domain) - - # restore settings - for model in get_models(include_auto_created=True): - model._meta.managed = model._meta.was_managed - - def _set_managed_apps(self, included_apps): - """ sets which apps are managed by syncdb """ - for model in get_models(include_auto_created=True): - model._meta.managed = False - - verbosity = int(self.options.get('verbosity')) - for app_model in get_apps(): - app_name = app_model.__name__.replace('.models', '') - if app_name in included_apps: - for model in get_models(app_model, include_auto_created=True): - model._meta.managed = model._meta.was_managed - if model._meta.managed and verbosity >= 3: - self._notice("=== Include Model: %s: %s" % (app_name, model.__name__)) - - def _sync_tenant(self, tenant): - self._notice("=== Running syncdb for schema: %s" % tenant.schema_name) - connection.set_tenant(tenant, include_public=False) - SyncdbCommand().execute(**self.options) - - def sync_tenant_apps(self, schema_name=None): - apps = self.tenant_apps or self.installed_apps - self._set_managed_apps(apps) - if schema_name: - tenant = TenantMiddleware.get_tenant_by_hostname(schema_name) - self._sync_tenant(tenant) - else: - all_tenants = TenantMiddleware.get_tenants() - if not all_tenants: - self._notice("No tenants found!") - - for tenant in all_tenants: - self._sync_tenant(tenant) - - def sync_public_apps(self): - self._notice("=== Running syncdb for schema public") - apps = self.shared_apps or self.installed_apps - self._set_managed_apps(apps) - SyncdbCommand().execute(**self.options) diff --git a/entrouvert/djommon/multitenant/management/commands/syncdb.py b/entrouvert/djommon/multitenant/management/commands/syncdb.py deleted file mode 100644 index b37ce4f..0000000 --- a/entrouvert/djommon/multitenant/management/commands/syncdb.py +++ /dev/null @@ -1,26 +0,0 @@ -# this file derive from django-tenant-schemas -# Author: Bernardo Pires Carneiro -# Email: carneiro.be@gmail.com -# License: MIT license -# Home-page: http://github.com/bcarneiro/django-tenant-schemas -from django.core.management.base import CommandError -from django.conf import settings -from tenant_schemas.utils import django_is_in_test_mode - -if 'south' in settings.INSTALLED_APPS: - from south.management.commands import syncdb -else: - from django.core.management.commands import syncdb - - -class Command(syncdb.Command): - - def handle(self, *args, **options): - database = options.get('database', 'default') - if (settings.DATABASES[database]['ENGINE'] == 'tenant_schemas.postgresql_backend' and not - django_is_in_test_mode()): - raise CommandError("syncdb has been disabled, for database '{0}'. " - "Use sync_schemas instead. Please read the " - "documentation if you don't know why " - "you shouldn't call syncdb directly!".format(database)) - super(Command, self).handle(*args, **options) diff --git a/entrouvert/djommon/multitenant/management/commands/tenant_command.py b/entrouvert/djommon/multitenant/management/commands/tenant_command.py deleted file mode 100644 index a1e08e1..0000000 --- a/entrouvert/djommon/multitenant/management/commands/tenant_command.py +++ /dev/null @@ -1,40 +0,0 @@ -# this file derive from django-tenant-schemas -# Author: Bernardo Pires Carneiro -# Email: carneiro.be@gmail.com -# License: MIT license -# Home-page: http://github.com/bcarneiro/django-tenant-schemas -from optparse import make_option -from django.core.management.base import BaseCommand, CommandError -from django.core.management import call_command, get_commands, load_command_class -from django.db import connection -from entrouvert.djommon.multitenant.management.commands import InteractiveTenantOption - - -class Command(InteractiveTenantOption, BaseCommand): - help = "Wrapper around django commands for use with an individual tenant" - - def run_from_argv(self, argv): - """ - Changes the option_list to use the options from the wrapped command. - Adds schema parameter to specifiy which schema will be used when - executing the wrapped command. - """ - # load the command object. - try: - app_name = get_commands()[argv[2]] - except KeyError: - raise CommandError("Unknown command: %r" % argv[2]) - - if isinstance(app_name, BaseCommand): - # if the command is already loaded, use it directly. - klass = app_name - else: - klass = load_command_class(app_name, argv[2]) - - super(Command, self).run_from_argv(argv) - - def handle(self, *args, **options): - tenant = self.get_tenant_from_options_or_interactive(**options) - connection.set_tenant(tenant) - - call_command(*args, **options) diff --git a/entrouvert/djommon/multitenant/middleware.py b/entrouvert/djommon/multitenant/middleware.py deleted file mode 100644 index f805a25..0000000 --- a/entrouvert/djommon/multitenant/middleware.py +++ /dev/null @@ -1,180 +0,0 @@ -import os -import json -import glob - -from django.conf import settings, UserSettingsHolder -from django.db import connection -from django.http import Http404 -from django.contrib.contenttypes.models import ContentType -from tenant_schemas.utils import get_tenant_model, remove_www_and_dev, get_public_schema_name - -SENTINEL = object() - -class TenantNotFound(RuntimeError): - pass - -class TenantMiddleware(object): - """ - This middleware should be placed at the very top of the middleware stack. - Selects the proper database schema using the request host. Can fail in - various ways which is better than corrupting or revealing data... - """ - @classmethod - def base(cls): - return settings.TENANT_BASE - - @classmethod - def hostname2schema(cls, hostname): - '''Convert hostname to PostgreSQL schema name''' - if hostname in getattr(settings, 'TENANT_MAPPING', {}): - return settings.TENANT_MAPPING[hostname] - return hostname.replace('.', '_').replace('-', '_') - - @classmethod - def get_tenant_by_hostname(cls, hostname): - '''Retrieve a tenant object for this hostname''' - if not os.path.exists(os.path.join(cls.base(), hostname)): - raise TenantNotFound - schema = cls.hostname2schema(hostname) - return get_tenant_model()(schema_name=schema, domain_url=hostname) - - @classmethod - def get_tenants(cls): - self = cls() - for path in glob.glob(os.path.join(cls.base(), '*')): - hostname = os.path.basename(path) - yield get_tenant_model()( - schema_name=self.hostname2schema(hostname), - domain_url=hostname) - - def process_request(self, request): - # connection needs first to be at the public schema, as this is where the - # tenant informations are saved - connection.set_schema_to_public() - hostname_without_port = remove_www_and_dev(request.get_host().split(':')[0]) - - try: - request.tenant = self.get_tenant_by_hostname(hostname_without_port) - except TenantNotFound: - raise Http404 - connection.set_tenant(request.tenant) - - # content type can no longer be cached as public and tenant schemas have different - # models. if someone wants to change this, the cache needs to be separated between - # public and shared schemas. if this cache isn't cleared, this can cause permission - # problems. for example, on public, a particular model has id 14, but on the tenants - # it has the id 15. if 14 is cached instead of 15, the permissions for the wrong - # model will be fetched. - ContentType.objects.clear_cache() - - # do we have a public-specific token? - if hasattr(settings, 'PUBLIC_SCHEMA_URLCONF') and request.tenant.schema_name == get_public_schema_name(): - request.urlconf = settings.PUBLIC_SCHEMA_URLCONF - - - - -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.domain_url, 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(FileBasedTenantSettingBaseMiddleware): - '''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)) diff --git a/entrouvert/djommon/multitenant/models.py b/entrouvert/djommon/multitenant/models.py deleted file mode 100644 index 8acb0ac..0000000 --- a/entrouvert/djommon/multitenant/models.py +++ /dev/null @@ -1,11 +0,0 @@ -from tenant_schemas.models import TenantMixin - -class Tenant(TenantMixin): - # default true, schema will be automatically created and synced when it is saved - auto_create_schema = False - - def save(self): - pass - - def __unicode__(self): - return u'%s' % self.schema_name diff --git a/entrouvert/djommon/multitenant/storage.py b/entrouvert/djommon/multitenant/storage.py deleted file mode 100644 index 2bd3391..0000000 --- a/entrouvert/djommon/multitenant/storage.py +++ /dev/null @@ -1,24 +0,0 @@ -import os - -from django.conf import settings -from django.core.exceptions import SuspiciousOperation -from django.utils._os import safe_join - -from django.db import connection - -from django.core.files.storage import FileSystemStorage - -__all__ = ('TenantFileSystemStorage', ) - -class TenantFileSystemStorage(FileSystemStorage): - '''Lookup files first in $TENANT_BASE//media/ then in default location''' - def path(self, name): - if connection.tenant: - location = safe_join(settings.TENANT_BASE, connection.tenant.domain_url, 'media') - else: - location = self.location - try: - path = safe_join(location, name) - except ValueError: - raise SuspiciousOperation("Attempted access to '%s' denied." % name) - return os.path.normpath(path) diff --git a/entrouvert/djommon/multitenant/template_loader.py b/entrouvert/djommon/multitenant/template_loader.py deleted file mode 100644 index 6e0190c..0000000 --- a/entrouvert/djommon/multitenant/template_loader.py +++ /dev/null @@ -1,115 +0,0 @@ -""" -Wrapper class that takes a list of template loaders as an argument and attempts -to load templates from them in order, caching the result. -""" - -import hashlib -from django.conf import settings -from django.core.exceptions import ImproperlyConfigured -from django.template.base import TemplateDoesNotExist -from django.template.loader import BaseLoader, get_template_from_string, find_template_loader, make_origin -from django.utils.encoding import force_bytes -from django.utils._os import safe_join -from django.db import connection - -class CachedLoader(BaseLoader): - is_usable = True - - def __init__(self, loaders): - self.template_cache = {} - self._loaders = loaders - self._cached_loaders = [] - - @property - def loaders(self): - # Resolve loaders on demand to avoid circular imports - if not self._cached_loaders: - # Set self._cached_loaders atomically. Otherwise, another thread - # could see an incomplete list. See #17303. - cached_loaders = [] - for loader in self._loaders: - cached_loaders.append(find_template_loader(loader)) - self._cached_loaders = cached_loaders - return self._cached_loaders - - def find_template(self, name, dirs=None): - for loader in self.loaders: - try: - template, display_name = loader(name, dirs) - return (template, make_origin(display_name, loader, name, dirs)) - except TemplateDoesNotExist: - pass - raise TemplateDoesNotExist(name) - - def load_template(self, template_name, template_dirs=None): - if connection.tenant: - key = '-'.join([str(connection.tenant.pk), template_name]) - else: - key = template_name - if template_dirs: - # If template directories were specified, use a hash to differentiate - if connection.tenant: - key = '-'.join([str(connection.tenant.schema_name), template_name, hashlib.sha1(force_bytes('|'.join(template_dirs))).hexdigest()]) - else: - key = '-'.join([template_name, hashlib.sha1(force_bytes('|'.join(template_dirs))).hexdigest()]) - - if key not in self.template_cache: - template, origin = self.find_template(template_name, template_dirs) - if not hasattr(template, 'render'): - try: - template = get_template_from_string(template, origin, template_name) - except TemplateDoesNotExist: - # If compiling the template we found raises TemplateDoesNotExist, - # back off to returning the source and display name for the template - # we were asked to load. This allows for correct identification (later) - # of the actual template that does not exist. - return template, origin - self.template_cache[key] = template - return self.template_cache[key], None - - def reset(self): - "Empty the template cache." - self.template_cache.clear() - -class FilesystemLoader(BaseLoader): - is_usable = True - - def get_template_sources(self, template_name, template_dirs=None): - """ - Returns the absolute paths to "template_name", when appended to each - directory in "template_dirs". Any paths that don't lie inside one of the - template dirs are excluded from the result set, for security reasons. - """ - if not connection.tenant: - return - if not template_dirs: - try: - template_dirs = settings.TENANT_TEMPLATE_DIRS - except AttributeError: - raise ImproperlyConfigured('To use %s.%s you must define the TENANT_TEMPLATE_DIRS' % (__name__, FilesystemLoader.__name__)) - for template_dir in template_dirs: - try: - yield safe_join(template_dir, connection.tenant.domain_url, 'templates', template_name) - except UnicodeDecodeError: - # The template dir name was a bytestring that wasn't valid UTF-8. - raise - except ValueError: - # The joined path was located outside of this particular - # template_dir (it might be inside another one, so this isn't - # fatal). - pass - - def load_template_source(self, template_name, template_dirs=None): - tried = [] - for filepath in self.get_template_sources(template_name, template_dirs): - try: - with open(filepath, 'rb') as fp: - return (fp.read().decode(settings.FILE_CHARSET), filepath) - except IOError: - tried.append(filepath) - if tried: - error_msg = "Tried %s" % tried - else: - error_msg = "Your TENANT_TEMPLATE_DIRS setting is empty. Change it to point to at least one template directory." - raise TemplateDoesNotExist(error_msg) - load_template_source.is_usable = True diff --git a/entrouvert/djommon/multitenant/tests.py b/entrouvert/djommon/multitenant/tests.py deleted file mode 100644 index 92272a3..0000000 --- a/entrouvert/djommon/multitenant/tests.py +++ /dev/null @@ -1,128 +0,0 @@ -""" -Test multitenant framework -""" - -import tempfile -import shutil -import os -import json -import StringIO - -from django.conf.urls import patterns -from django.test import TestCase, Client -from django.http import HttpResponse -from django.template.response import TemplateResponse - -try: - from django.test import override_settings -except ImportError: # django < 1.7 - from django.test.utils import override_settings - - -def json_key(request, *args, **kwargs): - from django.conf import settings - return HttpResponse(settings.JSON_KEY + ' json') - -def python_key(request, *args, **kwargs): - from django.conf import settings - return HttpResponse(settings.PYTHON_KEY + ' python') - -def template(request, *args, **kwargs): - return TemplateResponse(request, 'tenant.html') - -def upload(request): - from django.core.files.storage import default_storage - default_storage.save('upload', request.FILES['upload']) - return HttpResponse('') - -def download(request): - from django.core.files.storage import default_storage - return HttpResponse(default_storage.open('upload').read()) - -urlpatterns = patterns('', - ('^json_key/$', json_key), - ('^python_key/$', python_key), - ('^template/$', template), - ('^upload/$', upload), - ('^download/$', download), -) - -@override_settings( - ROOT_URLCONF=__name__, - MIDDLEWARE_CLASSES=( - 'entrouvert.djommon.multitenant.middleware.TenantMiddleware', - 'entrouvert.djommon.multitenant.middleware.JSONSettingsMiddleware', - 'entrouvert.djommon.multitenant.middleware.PythonSettingsMiddleware', - ), - TEMPLATE_LOADERS = ( - 'entrouvert.djommon.multitenant.template_loader.FilesystemLoader', - ), - DEFAULT_FILE_STORAGE = 'entrouvert.djommon.multitenant.storage.TenantFileSystemStorage', -) -class SimpleTest(TestCase): - TENANTS = ['tenant1', 'tenant2'] - - def setUp(self): - self.tenant_base = tempfile.mkdtemp() - for tenant in self.TENANTS: - tenant_dir = os.path.join(self.tenant_base, tenant) - os.mkdir(tenant_dir) - settings_py = os.path.join(tenant_dir, 'settings.json') - with file(settings_py, 'w') as f: - json.dump({'JSON_KEY': tenant}, f) - settings_json = os.path.join(tenant_dir, 'settings.py') - with file(settings_json, 'w') as f: - print >>f, 'PYTHON_KEY = %r' % tenant - templates_dir = os.path.join(tenant_dir, 'templates') - os.mkdir(templates_dir) - tenant_html = os.path.join(templates_dir, 'tenant.html') - with file(tenant_html, 'w') as f: - print >>f, tenant + ' template', - media_dir = os.path.join(tenant_dir, 'media') - os.mkdir(media_dir) - - def tearDown(self): - shutil.rmtree(self.tenant_base, ignore_errors=True) - - def tenant_settings(self): - return self.settings( - TENANT_BASE=self.tenant_base, - TENANT_TEMPLATE_DIRS=(self.tenant_base,) - ) - - def test_tenants(self): - with self.tenant_settings(): - for tenant in self.TENANTS: - c = Client(HTTP_HOST=tenant) - response = c.get('/json_key/') - self.assertEqual(response.content, tenant + ' json') - response = c.get('/python_key/') - self.assertEqual(response.content, tenant + ' python') - response = c.get('/template/') - self.assertEqual(response.content, tenant + ' template') - - def test_list_tenants(self): - from entrouvert.djommon.multitenant.middleware import TenantMiddleware - from tenant_schemas.utils import get_tenant_model - - with self.tenant_settings(): - l1 = set(map(str, TenantMiddleware.get_tenants())) - l2 = set(str(get_tenant_model()(schema_name=tenant, - domain_url=tenant)) for tenant in self.TENANTS) - self.assertEquals(l1, l2) - - def test_storage(self): - from django.core.files.base import ContentFile - with self.tenant_settings(): - for tenant in self.TENANTS: - c = Client(HTTP_HOST=tenant) - uploaded_file_path = os.path.join(self.tenant_base, tenant, 'media', 'upload') - self.assertFalse(os.path.exists(uploaded_file_path), uploaded_file_path) - response = c.post('/upload/', {'upload': ContentFile(tenant + ' upload', name='upload.txt')}) - self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, '') - self.assertTrue(os.path.exists(uploaded_file_path)) - self.assertEqual(file(uploaded_file_path).read(), tenant + ' upload') - response = c.get('/download/') - self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, tenant + ' upload') diff --git a/entrouvert/djommon/multitenant/views.py b/entrouvert/djommon/multitenant/views.py deleted file mode 100644 index 60f00ef..0000000 --- a/entrouvert/djommon/multitenant/views.py +++ /dev/null @@ -1 +0,0 @@ -# Create your views here.