Codebase structure¶
Who is this for: contributors looking for the right place to make a change.
Repository layout¶
src/django_db_tenants/ the library (the only package that ships)
tests/ pytest suite
tests/_extra/testutils/ test utilities, importable as `testutils`
tests/demo/ example Django project, importable as `demo` and `notes`
docs/ this site (Zensical)
The library: src/django_db_tenants¶
| Module | Responsibility | Reference |
|---|---|---|
__init__.py |
Public entry points: get_current_tenant, get_current_tenant_alias, tenant_context, ensure_db_registered; __version__ |
API reference |
apps.py |
AppConfig (label = "django_db_tenants", BigAutoField); connects the signal handlers and registers DATABASE_STRATEGIES in ready() |
|
conf.py |
DB_TENANTS settings with defaults; dotted paths are imported on access |
Settings |
context.py |
The tenant context (ContextVar) and tenant_context() |
Context |
middleware.py |
TenantMiddleware: resolves the tenant of each request, sync and async |
Middleware |
resolvers.py |
resolve_by_host, resolve_by_header |
Resolvers |
router.py |
TenantRouter: shared apps to the shared database, the rest to the active tenant |
Router |
db.py |
Runtime registration of tenant connections | Database registration |
strategies.py |
Database strategies: the DbConnectionFactory protocol, DatabaseStrategy, the built-in TemplateOverlay (connection settings, create and drop per backend), the strategy registry |
Database strategies |
registry.py |
Cached Client lookups by host and by slug, cache invalidation |
Registry |
signals.py |
Invalidates the registry cache and unregisters connections when Client / ClientDomain rows change |
Architecture |
models.py |
Client, ClientDomain |
Model reference |
fields.py, crypto.py |
EncryptedTextField, EncryptedJSONField and the Fernet helpers behind them |
Encryption |
provisioning.py |
create_database, migrate_tenant, provision, drop_database, delegating to the strategy |
Provisioning |
tasks.py |
with_tenant, for_each_tenant for background work |
Background tasks |
cache.py |
tenant_key_func: per-tenant cache key namespaces |
Cache isolation |
exceptions.py |
TenantError, TenantNotFound, TenantInactive |
Exceptions |
admin.py |
ClientAdmin with the domain inline and the provisioning action |
Admin |
management/commands/ |
provision_tenant, migrate_tenants, tenant_shell |
CLI reference |
migrations/ |
Schema of Client and ClientDomain (shared database only) |
|
py.typed |
Marks the package as typed (PEP 561) |
How the modules depend on each other¶
flowchart LR
middleware --> resolvers --> registry --> models
middleware --> db
db --> strategies
provisioning --> strategies
models --> strategies
middleware --> context
router --> context
cache --> context
context -. lazy import .-> db
context -. lazy import .-> registry
tasks --> context
provisioning --> db
signals --> registry
signals --> db
models --> fields --> crypto
registry --> cache
Every module reads its settings through conf.app_settings. context.py imports db and registry inside tenant_context() because registry imports the models, which must not load before the app registry is ready.
Test utilities: tests/_extra/testutils¶
| Module | Contents |
|---|---|
settings.py |
Test settings: the demo's settings plus the selected database backend, a fast password hasher, a tenant-namespaced LocMemCache, a fixed ENCRYPTION_KEY, CONTROL_HOSTS = ["control.test"], the EnvironmentPassword strategy |
databases.py |
TEST_DB backend selection and .env loading |
plugin.py |
pytest fixtures: tenant_factory, acme, globex, tenant_db_name, tenant_template_db, plus two autouse fixtures |
tenants.py |
Creating, cloning and dropping tenant databases per backend; reset_tenant_state(), tenant_login() |
factories.py |
factory_boy factories: ClientFactory, ClientDomainFactory, UserFactory, NoteFactory |
strategies.py |
EnvironmentPassword, a custom database strategy registered by the test settings, and Unregistered, one that is not |
See Testing strategy for how they fit together.
Demo project: tests/demo¶
| Path | What it shows |
|---|---|
demo/settings.py |
Every setting the library needs, marked # django-db-tenants |
demo/urls.py |
Django's own login views (django.contrib.auth.urls): tenant users log in with a password stored in their tenant database |
demo/views.py |
request.tenant, get_current_tenant(), a health check on an exempt path |
notes/ |
An ordinary app whose tables live in every tenant database |
notes/tasks.py |
Background jobs with @with_tenant and for_each_tenant |
notes/management/commands/seed_demo.py |
Onboarding tenants with provision_tenant |
notes/management/commands/notes_report.py |
Reading every tenant database from one command |
Configuration files¶
| File | Configures |
|---|---|
pyproject.toml |
Package metadata, runtime dependencies (Django, cryptography, django-strategy-field) and extras (postgres, mysql), dependency groups, setuptools package discovery, Commitizen ([tool.commitizen]) |
uv.lock |
Locked versions of every dependency; kept current by the uv-lock hook |
ruff.toml |
Lint rules and formatting (line length 120, target Python 3.11) |
ty.toml |
Type checking: Python 3.11, extra import paths, per-file overrides |
pytest.toml |
pytest and pytest-django: test paths, Python path, settings module, strict options, warnings as errors |
.coveragerc.toml |
coverage.py: source package, branch coverage, report options |
tox.ini |
The Python × Django × database matrix plus lint, type and docs |
.github/workflows/ci.yml |
GitHub Actions: every tox environment, the package build (CI/CD and build) |
.github/workflows/docs.yml |
GitHub Actions: builds this site from develop and publishes it on GitHub Pages (Publish the documentation) |
.github/workflows/cla.yml |
GitHub Actions: the Contributor License Agreement check on pull requests (CLA.md), signatures stored on the cla-signatures branch |
.github/workflows/release.yml |
GitHub Actions: PyPI upload and GitHub release of a pushed v* tag (see RELEASE.md) |
.pre-commit-config.yaml |
Git hooks, on pre-commit and commit-msg |
zensical.toml |
This site: navigation, theme, Markdown extensions, mkdocstrings options |
tools/check_zensical_nav.py |
Pre-commit hook: every docs page is in the nav of zensical.toml |
docker-compose.yml |
PostgreSQL and MySQL for the server test backends |
.env.example, .envrc.example |
Templates for the local environment files |
.editorconfig |
Indentation, line endings, final newline |
.python-version |
Python version uv uses for the project environment (3.13) |
Next steps¶
See how the pieces work together in Design overview, and at runtime in Architecture.