-
Notifications
You must be signed in to change notification settings - Fork 424
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #402 from goodtune/issue-302
Refactor ImproperlyConfigured to system check framework
- Loading branch information
Showing
22 changed files
with
291 additions
and
93 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.coverage |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1 @@ | ||
"""wtf URL Configuration | ||
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 = [ | ||
] | ||
urlpatterns = [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[flake8] | ||
exclude = .tox,docs,build,migrations,__init__.py | ||
ignore = C901,E501,E731 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,40 +1 @@ | ||
from django.conf import settings | ||
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)) | ||
default_app_config = 'tenant_schemas.apps.TenantSchemaConfig' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
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. | ||
""" | ||
# Take the app_configs and turn them into *old style* application names. | ||
# This is what we expect in the SHARED_APPS and TENANT_APPS settings. | ||
INSTALLED_APPS = [ | ||
config.name | ||
for config in app_configs | ||
] | ||
|
||
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 INSTALLED_APPS[0] != 'tenant_schemas': | ||
errors.append( | ||
Warning("You should put 'tenant_schemas' first in 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(INSTALLED_APPS): | ||
delta = set(settings.TENANT_APPS).difference(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(INSTALLED_APPS): | ||
delta = set(settings.SHARED_APPS).difference(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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,3 @@ | ||
from django.db import models | ||
from tenant_schemas.models import TenantMixin | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
from django.apps 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): | ||
actual = best_practice(apps.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=[ | ||
'dts_test_app', | ||
'customers', | ||
'django.contrib.auth', | ||
'django.contrib.contenttypes', | ||
'tenant_schemas', | ||
'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' first in 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']), | ||
]) |
Oops, something went wrong.