pep8
This commit is contained in:
parent
ec37a5fb6b
commit
1019e0186c
|
@ -9,4 +9,4 @@ class DummyModel(models.Model):
|
|||
name = models.CharField(max_length=100)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
return self.name
|
||||
|
|
|
@ -2,4 +2,4 @@ from django import forms
|
|||
|
||||
|
||||
class GenerateUsersForm(forms.Form):
|
||||
pass
|
||||
pass
|
||||
|
|
|
@ -5,4 +5,4 @@ from tenant_schemas.models import TenantMixin
|
|||
class Client(TenantMixin):
|
||||
name = models.CharField(max_length=100)
|
||||
description = models.TextField(max_length=200)
|
||||
created_on = models.DateField(auto_now_add=True)
|
||||
created_on = models.DateField(auto_now_add=True)
|
||||
|
|
|
@ -78,7 +78,6 @@ STATICFILES_DIRS = (
|
|||
STATICFILES_FINDERS = (
|
||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||
# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
|
||||
)
|
||||
|
||||
# Make this unique, and don't share it with anybody.
|
||||
|
@ -88,7 +87,6 @@ SECRET_KEY = 'as-%*_93v=r5*p_7cu8-%o6b&x^g+q$#*e*fl)k)x0-t=%q0qa'
|
|||
TEMPLATE_LOADERS = (
|
||||
'django.template.loaders.filesystem.Loader',
|
||||
'django.template.loaders.app_directories.Loader',
|
||||
# 'django.template.loaders.eggs.Loader',
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from django.conf.urls import patterns
|
||||
from django.conf.urls import patterns, url
|
||||
from tenant_tutorial.views import HomeView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^$', HomeView.as_view()),
|
||||
)
|
||||
url(r'^$', HomeView.as_view()),
|
||||
)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from django.conf.urls import patterns
|
||||
from django.conf.urls import patterns, url
|
||||
from customers.views import TenantView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^$', TenantView.as_view()),
|
||||
)
|
||||
url(r'^$', TenantView.as_view()),
|
||||
)
|
||||
|
|
|
@ -3,4 +3,4 @@ from django.contrib.auth.management.commands import createsuperuser
|
|||
|
||||
|
||||
class Command(TenantWrappedCommand):
|
||||
COMMAND = createsuperuser.Command
|
||||
COMMAND = createsuperuser.Command
|
||||
|
|
|
@ -30,7 +30,7 @@ class Command(SyncCommon):
|
|||
for app in ignored_apps:
|
||||
app_label = app.split('.')[-1]
|
||||
settings.SOUTH_MIGRATION_MODULES[app_label] = 'ignore'
|
||||
|
||||
|
||||
self._clear_south_cache()
|
||||
|
||||
def _save_south_settings(self):
|
||||
|
|
|
@ -18,10 +18,6 @@ class Command(SyncCommon):
|
|||
def handle(self, *args, **options):
|
||||
super(Command, self).handle(*args, **options)
|
||||
|
||||
if 'tenant_schemas.routers.TenantSyncRouter' not in settings.DATABASE_ROUTERS:
|
||||
raise ImproperlyConfigured("DATABASE_ROUTERS setting must contain "
|
||||
"'tenant_schemas.routers.TenantSyncRouter'.")
|
||||
|
||||
if "south" in settings.INSTALLED_APPS:
|
||||
self.options["migrate"] = False
|
||||
|
||||
|
|
|
@ -2,14 +2,15 @@ from django.conf import settings
|
|||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import connection
|
||||
from django.shortcuts import get_object_or_404
|
||||
from tenant_schemas.utils import get_tenant_model, remove_www, get_public_schema_name
|
||||
from tenant_schemas.utils import (get_tenant_model, remove_www,
|
||||
get_public_schema_name)
|
||||
|
||||
|
||||
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...
|
||||
various ways which is better than corrupting or revealing data.
|
||||
"""
|
||||
def hostname_from_request(self, request):
|
||||
""" Extracts hostname from request. Used for custom requests filtering.
|
||||
|
@ -18,22 +19,24 @@ class TenantMiddleware(object):
|
|||
return remove_www(request.get_host().split(':')[0])
|
||||
|
||||
def process_request(self, request):
|
||||
# connection needs first to be at the public schema, as this is where the
|
||||
# tenant metadata is stored
|
||||
# Connection needs first to be at the public schema, as this is where
|
||||
# the tenant metadata is stored.
|
||||
connection.set_schema_to_public()
|
||||
hostname = self.hostname_from_request(request)
|
||||
|
||||
request.tenant = get_object_or_404(get_tenant_model(), domain_url=hostname)
|
||||
request.tenant = get_object_or_404(
|
||||
get_tenant_model(), domain_url=hostname)
|
||||
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.
|
||||
# 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?
|
||||
# Do we have a public-specific urlconf?
|
||||
if hasattr(settings, 'PUBLIC_SCHEMA_URLCONF') and request.tenant.schema_name == get_public_schema_name():
|
||||
request.urlconf = settings.PUBLIC_SCHEMA_URLCONF
|
||||
|
|
|
@ -8,17 +8,26 @@ from tenant_schemas.utils import get_public_schema_name
|
|||
|
||||
|
||||
class TenantMixin(models.Model):
|
||||
auto_drop_schema = False # USE THIS WITH CAUTION!
|
||||
# set this flag to true on a parent class if
|
||||
# you want the schema to be automatically
|
||||
# removed after tenant remove.
|
||||
"""
|
||||
All tenant models must inherit this class.
|
||||
"""
|
||||
|
||||
auto_create_schema = True # set this flag to false on a parent class if
|
||||
# you don't want the schema to be automatically
|
||||
# created upon save.
|
||||
auto_drop_schema = False
|
||||
"""
|
||||
USE THIS WITH CAUTION!
|
||||
Set this flag to true on a parent class if you want the schema to be
|
||||
automatically deleted if the tenant row gets deleted.
|
||||
"""
|
||||
|
||||
auto_create_schema = True
|
||||
"""
|
||||
Set this flag to false on a parent class if you don't want the schema
|
||||
to be automatically created upon save.
|
||||
"""
|
||||
|
||||
domain_url = models.CharField(max_length=128, unique=True)
|
||||
schema_name = models.CharField(max_length=63, unique=True, validators=[_check_schema_name])
|
||||
schema_name = models.CharField(max_length=63, unique=True,
|
||||
validators=[_check_schema_name])
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
@ -27,10 +36,11 @@ class TenantMixin(models.Model):
|
|||
is_new = self.pk is None
|
||||
|
||||
if is_new and connection.schema_name != get_public_schema_name():
|
||||
raise Exception("Can't create tenant outside the public schema. Current schema is %s."
|
||||
% connection.schema_name)
|
||||
raise Exception("Can't create tenant outside the public schema. "
|
||||
"Current schema is %s." % connection.schema_name)
|
||||
elif not is_new and connection.schema_name not in (self.schema_name, get_public_schema_name()):
|
||||
raise Exception("Can't update tenant outside it's own schema or the public schema. Current schema is %s."
|
||||
raise Exception("Can't update tenant outside it's own schema or "
|
||||
"the public schema. Current schema is %s."
|
||||
% connection.schema_name)
|
||||
|
||||
super(TenantMixin, self).save(*args, **kwargs)
|
||||
|
@ -40,17 +50,19 @@ class TenantMixin(models.Model):
|
|||
self.create_schema(check_if_exists=True, verbosity=verbosity)
|
||||
post_schema_sync.send(sender=TenantMixin, tenant=self)
|
||||
except:
|
||||
# We failed creating the tenant, delete what we created and re-raise the exception
|
||||
# We failed creating the tenant, delete what we created and
|
||||
# re-raise the exception
|
||||
self.delete(force_drop=True)
|
||||
raise
|
||||
|
||||
def delete(self, force_drop=False, *args, **kwargs):
|
||||
"""
|
||||
Drops the schema related to the tenant instance. Just drop the schema if the parent
|
||||
class model has the attribute auto_drop_schema set to True.
|
||||
Deletes this row. Drops the tenant's schema if the attribute
|
||||
auto_drop_schema set to True.
|
||||
"""
|
||||
if connection.schema_name not in (self.schema_name, get_public_schema_name()):
|
||||
raise Exception("Can't delete tenant outside it's own schema or the public schema. Current schema is %s."
|
||||
raise Exception("Can't delete tenant outside it's own schema or "
|
||||
"the public schema. Current schema is %s."
|
||||
% connection.schema_name)
|
||||
|
||||
if schema_exists(self.schema_name) and (self.auto_drop_schema or force_drop):
|
||||
|
@ -60,11 +72,12 @@ class TenantMixin(models.Model):
|
|||
|
||||
super(TenantMixin, self).delete(*args, **kwargs)
|
||||
|
||||
def create_schema(self, check_if_exists=False, sync_schema=True, verbosity=1):
|
||||
def create_schema(self, check_if_exists=False, sync_schema=True,
|
||||
verbosity=1):
|
||||
"""
|
||||
Creates the schema 'schema_name' for this tenant. Optionally checks if the schema
|
||||
already exists before creating it. Returns true if the schema was created, false
|
||||
otherwise.
|
||||
Creates the schema 'schema_name' for this tenant. Optionally checks if
|
||||
the schema already exists before creating it. Returns true if the
|
||||
schema was created, false otherwise.
|
||||
"""
|
||||
|
||||
# safety check
|
||||
|
@ -79,14 +92,15 @@ class TenantMixin(models.Model):
|
|||
transaction.commit_unless_managed()
|
||||
|
||||
if sync_schema:
|
||||
# default is faking all migrations and syncing directly to the current models state
|
||||
# Default is faking all migrations and syncing directly to the
|
||||
# current models state.
|
||||
fake_all_migrations = getattr(settings, 'TENANT_CREATION_FAKES_MIGRATIONS', True)
|
||||
|
||||
call_command('sync_schemas',
|
||||
schema_name=self.schema_name,
|
||||
tenant=True,
|
||||
public=False,
|
||||
interactive=False, # don't ask to create an admin user
|
||||
interactive=False, # don't ask to create an admin
|
||||
migrate_all=fake_all_migrations,
|
||||
verbosity=verbosity,
|
||||
)
|
||||
|
@ -99,4 +113,3 @@ class TenantMixin(models.Model):
|
|||
verbosity=verbosity)
|
||||
|
||||
connection.set_schema_to_public()
|
||||
return True
|
||||
|
|
|
@ -3,4 +3,4 @@ from django.dispatch import Signal
|
|||
post_schema_sync = Signal(providing_args=['tenant'])
|
||||
post_schema_sync.__doc__ = """
|
||||
Sent after a tenant has been saved, its schema created and synced
|
||||
"""
|
||||
"""
|
||||
|
|
|
@ -7,7 +7,8 @@ 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.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
|
||||
|
@ -37,7 +38,7 @@ class CachedLoader(BaseLoader):
|
|||
for loader in self.loaders:
|
||||
try:
|
||||
template, display_name = loader(name, dirs)
|
||||
return (template, make_origin(display_name, loader, name, dirs))
|
||||
return template, make_origin(display_name, loader, name, dirs)
|
||||
except TemplateDoesNotExist:
|
||||
pass
|
||||
raise TemplateDoesNotExist(name)
|
||||
|
@ -48,7 +49,8 @@ class CachedLoader(BaseLoader):
|
|||
else:
|
||||
key = template_name
|
||||
if template_dirs:
|
||||
# If template directories were specified, use a hash to differentiate
|
||||
# If template directories were specified, use a hash to
|
||||
# differentiate
|
||||
if connection.tenant:
|
||||
key = '-'.join([str(connection.tenant.pk), template_name,
|
||||
hashlib.sha1(force_bytes('|'.join(template_dirs))).hexdigest()])
|
||||
|
|
|
@ -16,4 +16,4 @@ class SchemaURLNode(URLNode):
|
|||
|
||||
@register.tag
|
||||
def url(parser, token):
|
||||
return SchemaURLNode(default_url(parser,token))
|
||||
return SchemaURLNode(default_url(parser, token))
|
||||
|
|
|
@ -14,4 +14,4 @@ class NonAutoSyncTenant(TenantMixin):
|
|||
auto_create_schema = False
|
||||
|
||||
class Meta:
|
||||
app_label = 'tenant_schemas'
|
||||
app_label = 'tenant_schemas'
|
||||
|
|
|
@ -177,7 +177,7 @@ class TenantSyncTest(BaseTestCase):
|
|||
self.assertIn('django_session', tenant_tables)
|
||||
|
||||
def test_content_types_is_not_mandatory(self):
|
||||
"""
|
||||
"""
|
||||
Tests that even if content types is in SHARED_APPS, it's
|
||||
not required in TENANT_APPS.
|
||||
"""
|
||||
|
|
|
@ -49,4 +49,4 @@ class BaseTestCase(TestCase):
|
|||
interactive=False,
|
||||
migrate_all=True,
|
||||
verbosity=0,
|
||||
)
|
||||
)
|
||||
|
|
|
@ -55,11 +55,12 @@ def clean_tenant_url(url_string):
|
|||
|
||||
|
||||
def remove_www_and_dev(hostname):
|
||||
"""
|
||||
"""
|
||||
Legacy function - just in case someone is still using the old name
|
||||
"""
|
||||
return remove_www(hostname)
|
||||
|
||||
|
||||
def remove_www(hostname):
|
||||
"""
|
||||
Removes www. from the beginning of the address. Only for
|
||||
|
@ -102,4 +103,4 @@ def app_labels(apps_list):
|
|||
"""
|
||||
Returns a list of app labels of the given apps_list
|
||||
"""
|
||||
return [app.split('.')[-1] for app in apps_list]
|
||||
return [app.split('.')[-1] for app in apps_list]
|
||||
|
|
Loading…
Reference in New Issue