Refactor ImproperlyConfigured to system check framework
- ensure TENANT_APPS is defined in settings.py - ensure TENANT_MODEL is defined in settings.py - ensure TenantSyncRouter appears in DATABASE_ROUTERS - ensure public schema and any existing tenant schemas are not listed in PG_EXTRA_SEARCH_PATHS - raise error when TENANT_APPS is empty - issue warning when 'tenant_schemas' is not the last item in INSTALLED_APPS - issue warning when items in TENANT_APPS are not in INSTALLED_APPS - add checks for SHARED_APPS - add test cases for the best_practice system check - update install documentation which previously suggested concatenation - fix import path of get_public_schema_name - fix failing test case for TenantContextFilter - update tox.ini - add .travis.yml - PEP8 fixes
This commit is contained in:
parent
acb14ec5ce
commit
ea95f3f7a4
|
@ -0,0 +1,20 @@
|
||||||
|
# EditorConfig is awesome: http://EditorConfig.org
|
||||||
|
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# Unix-style newlines with a newline ending every file
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.{js,py}]
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
[*.py]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.{css,js,less}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
|
@ -103,7 +103,18 @@ To make use of shared and tenant-specific applications, there are two settings c
|
||||||
'myapp.houses',
|
'myapp.houses',
|
||||||
)
|
)
|
||||||
|
|
||||||
INSTALLED_APPS = list(SHARED_APPS) + [app for app in TENANT_APPS if app not in SHARED_APPS]
|
INSTALLED_APPS = (
|
||||||
|
'customers',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.sites',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.admin',
|
||||||
|
'myapp.hotels',
|
||||||
|
'myapp.houses',
|
||||||
|
'tenant_schemas',
|
||||||
|
)
|
||||||
|
|
||||||
You also have to set where your tenant model is.
|
You also have to set where your tenant model is.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
.coverage
|
|
@ -48,7 +48,16 @@ TENANT_MODEL = "customers.Client" # app.Model
|
||||||
|
|
||||||
TEST_RUNNER = 'django.test.runner.DiscoverRunner'
|
TEST_RUNNER = 'django.test.runner.DiscoverRunner'
|
||||||
|
|
||||||
INSTALLED_APPS = list(set(TENANT_APPS + SHARED_APPS))
|
INSTALLED_APPS = (
|
||||||
|
'dts_test_app',
|
||||||
|
'customers',
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.staticfiles',
|
||||||
|
'tenant_schemas',
|
||||||
|
)
|
||||||
|
|
||||||
MIDDLEWARE_CLASSES = (
|
MIDDLEWARE_CLASSES = (
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
|
|
@ -1,21 +1 @@
|
||||||
"""wtf URL Configuration
|
urlpatterns = []
|
||||||
|
|
||||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
|
||||||
https://docs.djangoproject.com/en/1.9/topics/http/urls/
|
|
||||||
Examples:
|
|
||||||
Function views
|
|
||||||
1. Add an import: from my_app import views
|
|
||||||
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
|
|
||||||
Class-based views
|
|
||||||
1. Add an import: from other_app.views import Home
|
|
||||||
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
|
|
||||||
Including another URLconf
|
|
||||||
1. Add an import: from blog import urls as blog_urls
|
|
||||||
2. Import the include() function: from django.conf.urls import url, include
|
|
||||||
3. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls))
|
|
||||||
"""
|
|
||||||
from django.conf.urls import url
|
|
||||||
from django.contrib import admin
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
]
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import os
|
||||||
|
|
||||||
# Django settings for tenant_tutorial project.
|
# Django settings for tenant_tutorial project.
|
||||||
|
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
@ -121,7 +123,6 @@ PUBLIC_SCHEMA_URLCONF = 'tenant_tutorial.urls_public'
|
||||||
# Python dotted path to the WSGI application used by Django's runserver.
|
# Python dotted path to the WSGI application used by Django's runserver.
|
||||||
WSGI_APPLICATION = 'tenant_tutorial.wsgi.application'
|
WSGI_APPLICATION = 'tenant_tutorial.wsgi.application'
|
||||||
|
|
||||||
import os
|
|
||||||
TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), '..', 'templates').replace('\\', '/'),)
|
TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), '..', 'templates').replace('\\', '/'),)
|
||||||
|
|
||||||
SHARED_APPS = (
|
SHARED_APPS = (
|
||||||
|
|
|
@ -14,6 +14,7 @@ framework.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks
|
# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks
|
||||||
# if running multiple sites in the same mod_wsgi process. To fix this, use
|
# if running multiple sites in the same mod_wsgi process. To fix this, use
|
||||||
|
@ -24,7 +25,6 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tenant_tutorial.settings")
|
||||||
# This application object is used by any WSGI server configured to use this
|
# This application object is used by any WSGI server configured to use this
|
||||||
# file. This includes Django's development server, if the WSGI_APPLICATION
|
# file. This includes Django's development server, if the WSGI_APPLICATION
|
||||||
# setting points here.
|
# setting points here.
|
||||||
from django.core.wsgi import get_wsgi_application
|
|
||||||
application = get_wsgi_application()
|
application = get_wsgi_application()
|
||||||
|
|
||||||
# Apply WSGI middleware here.
|
# Apply WSGI middleware here.
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
[flake8]
|
||||||
|
exclude = .tox,docs,build,migrations,__init__.py
|
||||||
|
ignore = C901,E501,E731
|
|
@ -1,40 +1 @@
|
||||||
from django.conf import settings
|
default_app_config = 'tenant_schemas.apps.TenantSchemaConfig'
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
|
||||||
|
|
||||||
from tenant_schemas.utils import get_public_schema_name, get_tenant_model
|
|
||||||
|
|
||||||
recommended_config = """
|
|
||||||
Warning: You should put 'tenant_schemas' at the end of INSTALLED_APPS:
|
|
||||||
INSTALLED_APPS = TENANT_APPS + SHARED_APPS + ('tenant_schemas',)
|
|
||||||
This is necessary to overwrite built-in django management commands with
|
|
||||||
their schema-aware implementations.
|
|
||||||
"""
|
|
||||||
# Test for configuration recommendations. These are best practices,
|
|
||||||
# they avoid hard to find bugs and unexpected behaviour.
|
|
||||||
if not hasattr(settings, 'TENANT_APPS'):
|
|
||||||
raise ImproperlyConfigured('TENANT_APPS setting not set')
|
|
||||||
|
|
||||||
if not settings.TENANT_APPS:
|
|
||||||
raise ImproperlyConfigured("TENANT_APPS is empty. "
|
|
||||||
"Maybe you don't need this app?")
|
|
||||||
|
|
||||||
if not hasattr(settings, 'TENANT_MODEL'):
|
|
||||||
raise ImproperlyConfigured('TENANT_MODEL setting not set')
|
|
||||||
|
|
||||||
if 'tenant_schemas.routers.TenantSyncRouter' not in settings.DATABASE_ROUTERS:
|
|
||||||
raise ImproperlyConfigured("DATABASE_ROUTERS setting must contain "
|
|
||||||
"'tenant_schemas.routers.TenantSyncRouter'.")
|
|
||||||
|
|
||||||
if hasattr(settings, 'PG_EXTRA_SEARCH_PATHS'):
|
|
||||||
if get_public_schema_name() in settings.PG_EXTRA_SEARCH_PATHS:
|
|
||||||
raise ImproperlyConfigured(
|
|
||||||
"%s can not be included on PG_EXTRA_SEARCH_PATHS."
|
|
||||||
% get_public_schema_name())
|
|
||||||
|
|
||||||
# make sure no tenant schema is in settings.PG_EXTRA_SEARCH_PATHS
|
|
||||||
invalid_schemas = set(settings.PG_EXTRA_SEARCH_PATHS).intersection(
|
|
||||||
get_tenant_model().objects.all().values_list('schema_name', flat=True))
|
|
||||||
if invalid_schemas:
|
|
||||||
raise ImproperlyConfigured(
|
|
||||||
"Do not include tenant schemas (%s) on PG_EXTRA_SEARCH_PATHS."
|
|
||||||
% list(invalid_schemas))
|
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.checks import Critical, Error, Warning, register
|
||||||
|
|
||||||
|
from tenant_schemas.utils import get_public_schema_name, get_tenant_model
|
||||||
|
|
||||||
|
|
||||||
|
class TenantSchemaConfig(AppConfig):
|
||||||
|
name = 'tenant_schemas'
|
||||||
|
|
||||||
|
|
||||||
|
@register('config')
|
||||||
|
def best_practice(app_configs, **kwargs):
|
||||||
|
"""
|
||||||
|
Test for configuration recommendations. These are best practices, they
|
||||||
|
avoid hard to find bugs and unexpected behaviour.
|
||||||
|
"""
|
||||||
|
if not hasattr(settings, 'TENANT_APPS'):
|
||||||
|
return [Critical('TENANT_APPS setting not set')]
|
||||||
|
|
||||||
|
if not hasattr(settings, 'TENANT_MODEL'):
|
||||||
|
return [Critical('TENANT_MODEL setting not set')]
|
||||||
|
|
||||||
|
if not hasattr(settings, 'SHARED_APPS'):
|
||||||
|
return [Critical('SHARED_APPS setting not set')]
|
||||||
|
|
||||||
|
if 'tenant_schemas.routers.TenantSyncRouter' not in settings.DATABASE_ROUTERS:
|
||||||
|
return [
|
||||||
|
Critical("DATABASE_ROUTERS setting must contain "
|
||||||
|
"'tenant_schemas.routers.TenantSyncRouter'.")
|
||||||
|
]
|
||||||
|
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
if settings.INSTALLED_APPS[-1] != 'tenant_schemas':
|
||||||
|
errors.append(
|
||||||
|
Warning("You should put 'tenant_schemas' at the end of INSTALLED_APPS.",
|
||||||
|
obj="django.conf.settings",
|
||||||
|
hint="This is necessary to overwrite built-in django "
|
||||||
|
"management commands with their schema-aware "
|
||||||
|
"implementations."))
|
||||||
|
|
||||||
|
if not settings.TENANT_APPS:
|
||||||
|
errors.append(
|
||||||
|
Error("TENANT_APPS is empty.",
|
||||||
|
hint="Maybe you don't need this app?"))
|
||||||
|
|
||||||
|
if hasattr(settings, 'PG_EXTRA_SEARCH_PATHS'):
|
||||||
|
if get_public_schema_name() in settings.PG_EXTRA_SEARCH_PATHS:
|
||||||
|
errors.append(Critical(
|
||||||
|
"%s can not be included on PG_EXTRA_SEARCH_PATHS."
|
||||||
|
% get_public_schema_name()))
|
||||||
|
|
||||||
|
# make sure no tenant schema is in settings.PG_EXTRA_SEARCH_PATHS
|
||||||
|
invalid_schemas = set(settings.PG_EXTRA_SEARCH_PATHS).intersection(
|
||||||
|
get_tenant_model().objects.all().values_list('schema_name', flat=True))
|
||||||
|
if invalid_schemas:
|
||||||
|
errors.append(Critical(
|
||||||
|
"Do not include tenant schemas (%s) on PG_EXTRA_SEARCH_PATHS."
|
||||||
|
% ", ".join(sorted(invalid_schemas))))
|
||||||
|
|
||||||
|
if not settings.SHARED_APPS:
|
||||||
|
errors.append(
|
||||||
|
Warning("SHARED_APPS is empty."))
|
||||||
|
|
||||||
|
if not set(settings.TENANT_APPS).issubset(settings.INSTALLED_APPS):
|
||||||
|
delta = set(settings.TENANT_APPS).difference(settings.INSTALLED_APPS)
|
||||||
|
errors.append(
|
||||||
|
Error("You have TENANT_APPS that are not in INSTALLED_APPS",
|
||||||
|
hint=[a for a in settings.TENANT_APPS if a in delta]))
|
||||||
|
|
||||||
|
if not set(settings.SHARED_APPS).issubset(settings.INSTALLED_APPS):
|
||||||
|
delta = set(settings.SHARED_APPS).difference(settings.INSTALLED_APPS)
|
||||||
|
errors.append(
|
||||||
|
Error("You have SHARED_APPS that are not in INSTALLED_APPS",
|
||||||
|
hint=[a for a in settings.SHARED_APPS if a in delta]))
|
||||||
|
|
||||||
|
return errors
|
|
@ -4,6 +4,9 @@ from django.conf import settings
|
||||||
from django.core.management.commands.migrate import Command as MigrateCommand
|
from django.core.management.commands.migrate import Command as MigrateCommand
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
|
|
||||||
|
from tenant_schemas.management.commands import SyncCommon
|
||||||
|
from tenant_schemas.utils import get_tenant_model, get_public_schema_name, schema_exists
|
||||||
|
|
||||||
if django.VERSION >= (1, 9, 0):
|
if django.VERSION >= (1, 9, 0):
|
||||||
from django.db.migrations.exceptions import MigrationSchemaMissing
|
from django.db.migrations.exceptions import MigrationSchemaMissing
|
||||||
else:
|
else:
|
||||||
|
@ -11,10 +14,6 @@ else:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
from tenant_schemas.management.commands import SyncCommon
|
|
||||||
from tenant_schemas.utils import get_tenant_model, get_public_schema_name, schema_exists
|
|
||||||
|
|
||||||
|
|
||||||
class Command(SyncCommon):
|
class Command(SyncCommon):
|
||||||
help = "Updates database schema. Manages both apps with migrations and those without."
|
help = "Updates database schema. Manages both apps with migrations and those without."
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ from django.db import connection
|
||||||
from django.template.loaders.base import Loader as BaseLoader
|
from django.template.loaders.base import Loader as BaseLoader
|
||||||
|
|
||||||
from tenant_schemas.postgresql_backend.base import FakeTenant
|
from tenant_schemas.postgresql_backend.base import FakeTenant
|
||||||
import django
|
|
||||||
|
|
||||||
class CachedLoader(BaseLoader):
|
class CachedLoader(BaseLoader):
|
||||||
is_usable = True
|
is_usable = True
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
from django.db import models
|
|
||||||
from tenant_schemas.models import TenantMixin
|
from tenant_schemas.models import TenantMixin
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
from django.apps.registry import Apps
|
||||||
|
from django.core.checks import Critical, Error, Warning
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.test.utils import override_settings
|
||||||
|
|
||||||
|
from tenant_schemas.apps import best_practice
|
||||||
|
from tenant_schemas.utils import get_tenant_model
|
||||||
|
|
||||||
|
|
||||||
|
class AppConfigTests(TestCase):
|
||||||
|
|
||||||
|
maxDiff = None
|
||||||
|
|
||||||
|
def assertBestPractice(self, expected):
|
||||||
|
from django.conf import settings
|
||||||
|
registry = Apps(settings.INSTALLED_APPS)
|
||||||
|
actual = best_practice(registry.get_app_configs())
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
@override_settings()
|
||||||
|
def test_unset_tenant_apps(self):
|
||||||
|
from django.conf import settings
|
||||||
|
del settings.TENANT_APPS
|
||||||
|
self.assertBestPractice([
|
||||||
|
Critical('TENANT_APPS setting not set'),
|
||||||
|
])
|
||||||
|
|
||||||
|
@override_settings()
|
||||||
|
def test_unset_tenant_model(self):
|
||||||
|
from django.conf import settings
|
||||||
|
del settings.TENANT_MODEL
|
||||||
|
self.assertBestPractice([
|
||||||
|
Critical('TENANT_MODEL setting not set'),
|
||||||
|
])
|
||||||
|
|
||||||
|
@override_settings()
|
||||||
|
def test_unset_shared_apps(self):
|
||||||
|
from django.conf import settings
|
||||||
|
del settings.SHARED_APPS
|
||||||
|
self.assertBestPractice([
|
||||||
|
Critical('SHARED_APPS setting not set'),
|
||||||
|
])
|
||||||
|
|
||||||
|
@override_settings(DATABASE_ROUTERS=())
|
||||||
|
def test_database_routers(self):
|
||||||
|
self.assertBestPractice([
|
||||||
|
Critical("DATABASE_ROUTERS setting must contain "
|
||||||
|
"'tenant_schemas.routers.TenantSyncRouter'."),
|
||||||
|
])
|
||||||
|
|
||||||
|
@override_settings(INSTALLED_APPS=[
|
||||||
|
'tenant_schemas',
|
||||||
|
'dts_test_app',
|
||||||
|
'customers',
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.staticfiles',
|
||||||
|
])
|
||||||
|
def test_tenant_schemas_last_installed_apps(self):
|
||||||
|
self.assertBestPractice([
|
||||||
|
Warning("You should put 'tenant_schemas' at the end of INSTALLED_APPS.",
|
||||||
|
obj="django.conf.settings",
|
||||||
|
hint="This is necessary to overwrite built-in django "
|
||||||
|
"management commands with their schema-aware "
|
||||||
|
"implementations."),
|
||||||
|
])
|
||||||
|
|
||||||
|
@override_settings(TENANT_APPS=())
|
||||||
|
def test_tenant_apps_empty(self):
|
||||||
|
self.assertBestPractice([
|
||||||
|
Error("TENANT_APPS is empty.",
|
||||||
|
hint="Maybe you don't need this app?"),
|
||||||
|
])
|
||||||
|
|
||||||
|
@override_settings(PG_EXTRA_SEARCH_PATHS=['public', 'demo1', 'demo2'])
|
||||||
|
def test_public_schema_on_extra_search_paths(self):
|
||||||
|
TenantModel = get_tenant_model()
|
||||||
|
TenantModel.objects.create(
|
||||||
|
schema_name='demo1', domain_url='demo1.example.com')
|
||||||
|
TenantModel.objects.create(
|
||||||
|
schema_name='demo2', domain_url='demo2.example.com')
|
||||||
|
self.assertBestPractice([
|
||||||
|
Critical("public can not be included on PG_EXTRA_SEARCH_PATHS."),
|
||||||
|
Critical("Do not include tenant schemas (demo1, demo2) on PG_EXTRA_SEARCH_PATHS."),
|
||||||
|
])
|
||||||
|
|
||||||
|
@override_settings(SHARED_APPS=())
|
||||||
|
def test_shared_apps_empty(self):
|
||||||
|
self.assertBestPractice([
|
||||||
|
Warning("SHARED_APPS is empty."),
|
||||||
|
])
|
||||||
|
|
||||||
|
@override_settings(TENANT_APPS=(
|
||||||
|
'dts_test_app',
|
||||||
|
'django.contrib.flatpages',
|
||||||
|
))
|
||||||
|
def test_tenant_app_missing_from_install_apps(self):
|
||||||
|
self.assertBestPractice([
|
||||||
|
Error("You have TENANT_APPS that are not in INSTALLED_APPS",
|
||||||
|
hint=['django.contrib.flatpages']),
|
||||||
|
])
|
||||||
|
|
||||||
|
@override_settings(SHARED_APPS=(
|
||||||
|
'tenant_schemas',
|
||||||
|
'customers',
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.flatpages',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.staticfiles',
|
||||||
|
))
|
||||||
|
def test_shared_app_missing_from_install_apps(self):
|
||||||
|
self.assertBestPractice([
|
||||||
|
Error("You have SHARED_APPS that are not in INSTALLED_APPS",
|
||||||
|
hint=['django.contrib.flatpages']),
|
||||||
|
])
|
|
@ -1,16 +1,28 @@
|
||||||
import logging
|
import logging
|
||||||
|
from mock import patch
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from tenant_schemas import log
|
from tenant_schemas import log
|
||||||
|
|
||||||
|
|
||||||
|
@patch('tenant_schemas.log.connection.tenant', autospec=True,
|
||||||
|
schema_name='context')
|
||||||
class LoggingFilterTests(TestCase):
|
class LoggingFilterTests(TestCase):
|
||||||
|
|
||||||
def test_tenant_context_filter(self):
|
def test_tenant_context_filter(self, mock_connection):
|
||||||
|
mock_connection.domain_url = 'context.example.com'
|
||||||
filter_ = log.TenantContextFilter()
|
filter_ = log.TenantContextFilter()
|
||||||
record = logging.makeLogRecord({})
|
record = logging.makeLogRecord({})
|
||||||
res = filter_.filter(record)
|
res = filter_.filter(record)
|
||||||
self.assertEqual(res, True)
|
self.assertEqual(res, True)
|
||||||
self.assertEqual(record.schema_name, 'public')
|
self.assertEqual(record.schema_name, 'context')
|
||||||
|
self.assertEqual(record.domain_url, 'context.example.com')
|
||||||
|
|
||||||
|
def test_tenant_context_filter_blank_domain_url(self, mock_connection):
|
||||||
|
filter_ = log.TenantContextFilter()
|
||||||
|
record = logging.makeLogRecord({})
|
||||||
|
res = filter_.filter(record)
|
||||||
|
self.assertEqual(res, True)
|
||||||
|
self.assertEqual(record.schema_name, 'context')
|
||||||
self.assertEqual(record.domain_url, '')
|
self.assertEqual(record.domain_url, '')
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.test.client import RequestFactory
|
from django.test.client import RequestFactory
|
||||||
from tenant_schemas import get_public_schema_name
|
|
||||||
|
|
||||||
from tenant_schemas.middleware import TenantMiddleware
|
from tenant_schemas.middleware import TenantMiddleware
|
||||||
from tenant_schemas.tests.models import Tenant
|
from tenant_schemas.tests.models import Tenant
|
||||||
from tenant_schemas.tests.testcases import BaseTestCase
|
from tenant_schemas.tests.testcases import BaseTestCase
|
||||||
|
from tenant_schemas.utils import get_public_schema_name
|
||||||
|
|
||||||
|
|
||||||
class RoutesTestCase(BaseTestCase):
|
class RoutesTestCase(BaseTestCase):
|
||||||
|
|
|
@ -87,7 +87,7 @@ class TenantDataAndSettingsTest(BaseTestCase):
|
||||||
self.assertFalse(schema_exists('auto_drop_tenant'))
|
self.assertFalse(schema_exists('auto_drop_tenant'))
|
||||||
Tenant.auto_drop_schema = True
|
Tenant.auto_drop_schema = True
|
||||||
tenant = Tenant(domain_url='something.test.com',
|
tenant = Tenant(domain_url='something.test.com',
|
||||||
schema_name='auto_drop_tenant')
|
schema_name='auto_drop_tenant')
|
||||||
tenant.save(verbosity=BaseTestCase.get_verbosity())
|
tenant.save(verbosity=BaseTestCase.get_verbosity())
|
||||||
self.assertTrue(schema_exists(tenant.schema_name))
|
self.assertTrue(schema_exists(tenant.schema_name))
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import django
|
|
||||||
import inspect
|
import inspect
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
|
@ -27,7 +26,7 @@ class BaseTestCase(TestCase):
|
||||||
connection.set_schema_to_public()
|
connection.set_schema_to_public()
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.execute('DROP SCHEMA %s CASCADE; CREATE SCHEMA %s;'
|
cursor.execute('DROP SCHEMA %s CASCADE; CREATE SCHEMA %s;'
|
||||||
% (get_public_schema_name(), get_public_schema_name(), ))
|
% (get_public_schema_name(), get_public_schema_name()))
|
||||||
super(BaseTestCase, cls).setUpClass()
|
super(BaseTestCase, cls).setUpClass()
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
|
@ -6,10 +6,10 @@ from tenant_schemas.utils import clean_tenant_url
|
||||||
def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None,
|
def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None,
|
||||||
current_app=None):
|
current_app=None):
|
||||||
url = reverse_default(
|
url = reverse_default(
|
||||||
viewname=viewname,
|
viewname=viewname,
|
||||||
urlconf=urlconf,
|
urlconf=urlconf,
|
||||||
args=args,
|
args=args,
|
||||||
kwargs=kwargs,
|
kwargs=kwargs,
|
||||||
current_app=current_app
|
current_app=current_app
|
||||||
)
|
)
|
||||||
return clean_tenant_url(url)
|
return clean_tenant_url(url)
|
||||||
|
|
|
@ -55,8 +55,8 @@ def clean_tenant_url(url_string):
|
||||||
Removes the TENANT_TOKEN from a particular string
|
Removes the TENANT_TOKEN from a particular string
|
||||||
"""
|
"""
|
||||||
if hasattr(settings, 'PUBLIC_SCHEMA_URLCONF'):
|
if hasattr(settings, 'PUBLIC_SCHEMA_URLCONF'):
|
||||||
if (settings.PUBLIC_SCHEMA_URLCONF
|
if (settings.PUBLIC_SCHEMA_URLCONF and
|
||||||
and url_string.startswith(settings.PUBLIC_SCHEMA_URLCONF)):
|
url_string.startswith(settings.PUBLIC_SCHEMA_URLCONF)):
|
||||||
url_string = url_string[len(settings.PUBLIC_SCHEMA_URLCONF):]
|
url_string = url_string[len(settings.PUBLIC_SCHEMA_URLCONF):]
|
||||||
return url_string
|
return url_string
|
||||||
|
|
||||||
|
|
18
tox.ini
18
tox.ini
|
@ -4,16 +4,18 @@ envlist = py{27,35}-dj{18,19,110}
|
||||||
[testenv]
|
[testenv]
|
||||||
usedevelop = True
|
usedevelop = True
|
||||||
|
|
||||||
deps =
|
deps =
|
||||||
coverage
|
coverage
|
||||||
dj18: Django~=1.8.0
|
mock
|
||||||
dj19: Django~=1.9.0
|
tblib
|
||||||
dj110: Django~=1.10.0
|
dj18: Django~=1.8.0
|
||||||
|
dj19: Django~=1.9.0
|
||||||
|
dj110: Django~=1.10.0
|
||||||
|
|
||||||
changedir = dts_test_project
|
changedir = dts_test_project
|
||||||
|
|
||||||
passenv = PG_NAME PG_USER PG_PASSWORD PG_HOST
|
passenv = PG_NAME PG_USER PG_PASSWORD PG_HOST
|
||||||
|
|
||||||
commands =
|
commands =
|
||||||
coverage run manage.py test --noinput {posargs:tenant_schemas.tests -v 2}
|
coverage run manage.py test --noinput {posargs:tenant_schemas}
|
||||||
coverage report -m --include=../tenant_schemas/*
|
coverage report -m --include=../tenant_schemas/*
|
||||||
|
|
|
@ -30,11 +30,10 @@
|
||||||
# contains the following line:
|
# contains the following line:
|
||||||
#
|
#
|
||||||
# include VERSION
|
# include VERSION
|
||||||
|
from subprocess import Popen, PIPE
|
||||||
|
|
||||||
__all__ = ("get_git_version")
|
__all__ = ("get_git_version")
|
||||||
|
|
||||||
from subprocess import Popen, PIPE
|
|
||||||
|
|
||||||
|
|
||||||
def call_git_describe():
|
def call_git_describe():
|
||||||
try:
|
try:
|
||||||
|
|
Loading…
Reference in New Issue