Added test project to enable testing with additional models and apps. This allow us to test different apps in TENANT_APPS AND SHARED_APPS. Added more tests that ensure cross schema foreign keys are possible, although the constraints are not created.
This commit is contained in:
parent
1019e0186c
commit
9a9b8a00df
|
@ -7,6 +7,7 @@ include version.py
|
|||
include VERSION
|
||||
recursive-include docs *
|
||||
recursive-include examples *
|
||||
recursive-include dts_test_project
|
||||
|
||||
# exclude all bytecode
|
||||
global-exclude *.pyo
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
from django.db import models
|
||||
|
||||
|
||||
class DummyModel(models.Model):
|
||||
"""
|
||||
Just a test model so we can test manipulating data
|
||||
inside a tenant
|
||||
"""
|
||||
name = models.CharField(max_length=100)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
|
@ -0,0 +1 @@
|
|||
__author__ = 'bernardo'
|
|
@ -0,0 +1,8 @@
|
|||
from django.db import models
|
||||
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)
|
|
@ -0,0 +1,16 @@
|
|||
from django.contrib.auth.models import User
|
||||
from django.db import models
|
||||
|
||||
|
||||
class DummyModel(models.Model):
|
||||
"""
|
||||
Just a test model so we can test manipulating data inside a tenant
|
||||
"""
|
||||
name = models.CharField(max_length=100)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class ModelWithFkToPublicUser(models.Model):
|
||||
user = models.ForeignKey(User)
|
|
@ -0,0 +1,118 @@
|
|||
"""
|
||||
Django settings for dts_test_project project.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/1.6/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/1.6/ref/settings/
|
||||
"""
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
import os
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/1.6/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'cl1)b#c&xmm36z3e(quna-vb@ab#&gpjtdjtpyzh!qn%bc^xxn'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
TEMPLATE_DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
SHARED_APPS = (
|
||||
'tenant_schemas', # mandatory
|
||||
'customers', # you must list the app where your tenant model resides in
|
||||
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
)
|
||||
|
||||
TENANT_APPS = (
|
||||
'dts_test_app',
|
||||
)
|
||||
|
||||
TENANT_MODEL = "customers.Client" # app.Model
|
||||
|
||||
INSTALLED_APPS = TENANT_APPS + SHARED_APPS + ('tenant_schemas',)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'dts_test_project.urls'
|
||||
|
||||
WSGI_APPLICATION = 'dts_test_project.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/1.6/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'tenant_schemas.postgresql_backend',
|
||||
'NAME': 'dts_test_project',
|
||||
'USER': 'postgres',
|
||||
'PASSWORD': 'root',
|
||||
'HOST': 'localhost',
|
||||
'PORT': '',
|
||||
}
|
||||
}
|
||||
|
||||
DATABASE_ROUTERS = (
|
||||
'tenant_schemas.routers.TenantSyncRouter',
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
'tenant_tutorial.middleware.TenantTutorialMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
)
|
||||
|
||||
TEMPLATE_CONTEXT_PROCESSORS = (
|
||||
'django.core.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.core.context_processors.debug',
|
||||
'django.core.context_processors.media',
|
||||
'django.core.context_processors.static',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
)
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/1.6/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/1.6/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dts_test_project.settings")
|
||||
|
||||
from django.core.management import execute_from_command_line
|
||||
|
||||
execute_from_command_line(sys.argv)
|
|
@ -0,0 +1 @@
|
|||
/home/bernardo/projects/django-tenant-schemas/tenant_schemas
|
|
@ -1,7 +1,8 @@
|
|||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import connection
|
||||
|
||||
from dts_test_app.models import DummyModel
|
||||
from dts_test_app.models import DummyModel, ModelWithFkToPublicUser
|
||||
from tenant_schemas.test.cases import TenantTestCase
|
||||
from tenant_schemas.tests.models import Tenant, NonAutoSyncTenant
|
||||
from tenant_schemas.tests.testcases import BaseTestCase
|
||||
|
@ -197,6 +198,85 @@ class TenantSyncTest(BaseTestCase):
|
|||
self.assertIn('django_session', tenant_tables)
|
||||
|
||||
|
||||
class SharedAuthTest(BaseTestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(SharedAuthTest, cls).setUpClass()
|
||||
settings.SHARED_APPS = ('tenant_schemas',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes', )
|
||||
settings.TENANT_APPS = ('dts_test_app', )
|
||||
settings.INSTALLED_APPS = settings.SHARED_APPS + settings.TENANT_APPS
|
||||
cls.sync_shared()
|
||||
Tenant(domain_url='test.com', schema_name=get_public_schema_name()).save()
|
||||
|
||||
# Create a tenant
|
||||
cls.tenant = Tenant(domain_url='tenant.test.com', schema_name='tenant')
|
||||
cls.tenant.save()
|
||||
|
||||
# Create some users
|
||||
with schema_context(get_public_schema_name()): # this could actually also be executed inside a tenant
|
||||
cls.user1 = User(username='arbitrary-1', email="arb1@test.com")
|
||||
cls.user1.save()
|
||||
cls.user2 = User(username='arbitrary-2', email="arb2@test.com")
|
||||
cls.user2.save()
|
||||
|
||||
# Create instances on the tenant that point to the users on public
|
||||
with tenant_context(cls.tenant):
|
||||
cls.d1 = ModelWithFkToPublicUser(user=cls.user1)
|
||||
cls.d1.save()
|
||||
cls.d2 = ModelWithFkToPublicUser(user=cls.user2)
|
||||
cls.d2.save()
|
||||
|
||||
def test_cross_schema_constraint_gets_created(self):
|
||||
"""
|
||||
Tests that a foreign key constraint gets created even for cross schema references.
|
||||
"""
|
||||
sql = """
|
||||
SELECT
|
||||
tc.constraint_name, tc.table_name, kcu.column_name,
|
||||
ccu.table_name AS foreign_table_name,
|
||||
ccu.column_name AS foreign_column_name
|
||||
FROM
|
||||
information_schema.table_constraints AS tc
|
||||
JOIN information_schema.key_column_usage AS kcu
|
||||
ON tc.constraint_name = kcu.constraint_name
|
||||
JOIN information_schema.constraint_column_usage AS ccu
|
||||
ON ccu.constraint_name = tc.constraint_name
|
||||
WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name=%s
|
||||
"""
|
||||
cursor = connection.cursor()
|
||||
cursor.execute(sql, (ModelWithFkToPublicUser._meta.db_table, ))
|
||||
fk_constraints = cursor.fetchall()
|
||||
self.assertEqual(1, len(fk_constraints))
|
||||
|
||||
# The foreign key should reference the primary key of the user table
|
||||
fk = fk_constraints[0]
|
||||
self.assertEqual(User._meta.db_table, fk[3])
|
||||
self.assertEqual('id', fk[4])
|
||||
|
||||
def test_direct_relation_to_public(self):
|
||||
"""
|
||||
Tests that a forward relationship through a foreign key to public from a model inside TENANT_APPS works.
|
||||
"""
|
||||
with tenant_context(self.tenant):
|
||||
self.assertEqual(User.objects.get(pk=self.user1.id),
|
||||
ModelWithFkToPublicUser.objects.get(pk=self.d1.id).user)
|
||||
self.assertEqual(User.objects.get(pk=self.user2.id),
|
||||
ModelWithFkToPublicUser.objects.get(pk=self.d2.id).user)
|
||||
|
||||
def test_reverse_relation_to_public(self):
|
||||
"""
|
||||
Tests that a reverse relationship through a foreign keys to public from a model inside TENANT_APPS works.
|
||||
"""
|
||||
with tenant_context(self.tenant):
|
||||
users = User.objects.all().select_related().order_by('id')
|
||||
self.assertEqual(ModelWithFkToPublicUser.objects.get(pk=self.d1.id),
|
||||
users[0].modelwithfktopublicuser_set.all()[:1].get())
|
||||
self.assertEqual(ModelWithFkToPublicUser.objects.get(pk=self.d2.id),
|
||||
users[1].modelwithfktopublicuser_set.all()[:1].get())
|
||||
|
||||
|
||||
class TenantTestCaseTest(BaseTestCase, TenantTestCase):
|
||||
"""
|
||||
Tests that the tenant created inside TenantTestCase persists on
|
||||
|
|
Loading…
Reference in New Issue