I'm writing because I have a big problem. Well, I have a project in Django where I am using django-tenants. Unfortunately, I can't run any tests as these end up with the following error when calling migrations: ‘django.db.migrations.exceptions.MigrationSchemaMissing: Unable to create the django_migrations table (no schema has been selected to create in LINE 1: CREATE TABLE ‘django_migrations’ (‘id’ bigint NOT NULL PRIMA...’
The problem is quite acute. I am fed up with regression errors and would like to write tests for the code. I will appreciate any suggestion. If you have any suggestions for improvements to the project, I'd love to read about that too.
Project details below. Best regards
Dependencies
[tool.poetry.dependencies]
python = "^3.13"
django = "5.1.8" # The newest version is not compatible with django-tenants yet
django-tenants = "^3.7.0"
dj-database-url = "^2.3.0"
django-bootstrap5 = "^25.1"
django-bootstrap-icons = "^0.9.0"
uvicorn = "^0.34.0"
uvicorn-worker = "^0.3.0"
gunicorn = "^23.0.0"
whitenoise = "^6.8.2"
encrypt-decrypt-fields = "^1.3.6"
django-bootstrap-modal-forms = "^3.0.5"
django-model-utils = "^5.0.0"
werkzeug = "^3.1.3"
tzdata = "^2025.2"
pytz = "^2025.2"
psycopg = {extras = ["binary", "pool"], version = "^3.2.4"}
django-colorfield = "^0.13.0"
sentry-sdk = {extras = ["django"], version = "^2.25.1"}
Settings.py
import os
from pathlib import Path
from uuid import uuid4
# External Dependencies
import dj_database_url
from django.contrib.messages import constants as messages
from django.utils.translation import gettext_lazy as _
BASE_DIR = Path(__file__).resolve().parent.parent
PROJECT_DIR = os.path.join(BASE_DIR, os.pardir)
TENANT_APPS_DIR = BASE_DIR / "tenant"
DEBUG = os.environ.get("DEBUG", "False").lower() in ["true", "1", "yes"]
TEMPLATE_DEBUG = DEBUG
SECRET_KEY = os.environ.get("SECRET_KEY", str(uuid4())) if DEBUG else os.environ["SECRET_KEY"]
VERSION = os.environ.get("VERSION", "develop")
SECURE_SSL_REDIRECT = os.environ.get("SECURE_SSL_REDIRECT", "False").lower() in ["true", "1", "yes"]
try:
ALLOWED_HOSTS = os.environ["ALLOWED_HOSTS"].split(";")
except KeyError:
if not DEBUG:
raise
ALLOWED_HOSTS = ["localhost", ".localhost"]
DEFAULT_DOMAIN = os.environ.get("DEFAULT_DOMAIN", ALLOWED_HOSTS[0])
DEFAULT_FILE_STORAGE = "django_tenants.files.storage.TenantFileSystemStorage"
SHARED_APPS = [
"django_tenants",
"sfe.common.apps.CommonConfig",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]
TENANT_APPS = [
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"django_bootstrap5",
"django_bootstrap_icons",
"bootstrap_modal_forms",
"sfe.tenant.apps.TenantConfig",
"sfe.tenant.email_controller.apps.EmailControllerConfig",
]
INSTALLED_APPS = list(SHARED_APPS) + [app for app in TENANT_APPS if app not in SHARED_APPS]
MIDDLEWARE = [
"sfe.common.middleware.HealthCheckMiddleware",
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.locale.LocaleMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
if DEBUG:
INTERNAL_IPS = ["localhost", ".localhost"]
ROOT_URLCONF = "sfe.urls_tenant"
PUBLIC_SCHEMA_URLCONF = "sfe.urls_public"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
"sfe.common.context_processor.version",
"sfe.common.context_processor.default_domain",
],
},
},
]
WSGI_APPLICATION = "sfe.wsgi.application"
default_db = dj_database_url.config(engine="django_tenants.postgresql_backend")
DATABASES = {
"default": {
"OPTIONS": {"pool": True},
**default_db,
}
}
DATABASE_ROUTERS = ("django_tenants.routers.TenantSyncRouter",)
TEST_RUNNER = "django.test.runner.DiscoverRunner"
AUTH_PASSWORD_VALIDATORS = [
{"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"},
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
]
TENANT_MODEL = "common.SystemTenant"
TENANT_DOMAIN_MODEL = "common.Domain"
PUBLIC_SCHEMA_NAME = "public"
LANGUAGE_CODE = "pl"
LANGUAGES = [("pl", "Polski"), ("en", "English")]
LOCALE_PATHS = (os.path.join(BASE_DIR, "locale"),)
TIME_ZONE = "UTC"
USE_TZ = True
USE_I18N = True
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
STATIC_URL = "/static/"
STATIC_ROOT = os.path.join(PROJECT_ROOT, "static")
LOGIN_URL = _("/login/")
LOGOUT_REDIRECT_URL = "/"
LOGIN_REDIRECT_URL = "/"
SESSION_COOKIE_AGE = 86400
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
IMAP_TIMEOUT = 60
ADMINS = [("Damian Giebas", "[email protected]")]
MANAGERS = ADMINS
FIRST_DAY_OF_WEEK = 1
Simple test setup:
# External Dependencies
from django.utils.timezone import now
from django_tenants.test.cases import TenantTestCase
from django_tenants.utils import schema_context
# Current App
from sfe.tenant.models.due_date import DueDate
class DueDateModelTests(TenantTestCase):
def setUp(self):
super().setUp()
def test_create_due_date(self):
with schema_context(self.tenant.schema_name):
DueDate.objects.create(date=now().date(), name="TestDueDate")
assert DueDate.objects.all().count() == 1
EDIT: It turns out that the transition from django-tenant-schemas to django-tenants has not gone well. Unfortunately, running the migration on an empty database also ends with an error. Migrations in the tenant application do not execute. The problem is therefore not located in the configuration.
