Skip to main content

Pluggable multi-tenant toolkit for Django — schema & database backends, JWT auth, tenant-scoped middleware.

Project description

django-tenantkit

Pluggable multi-tenant toolkit for Django. Wraps django-tenants and adds tenant-scoped JWT authentication, flexible tenant resolution, and a full middleware stack. Supports both schema-per-tenant and database-per-tenant modes.

Features

  • Dual-mode tenancyschema (PostgreSQL schemas via django-tenants) or database (separate DB per tenant), switched by a single setting
  • Tenant resolutionX-Tenant header (domain or schema name) with host domain fallback
  • JWT authentication — local token decoding, tenant-scoped user lookup, result caching
  • Middleware stackTenantMiddlewareAuthMiddlewareBlockedUserMiddleware
  • DRF integrationTenantAuthentication, TenantUser, permission classes
  • ASGI compatible — context storage uses contextvars, safe under async Django
  • Database routerTenantDatabaseRouter for query routing across schemas/databases
  • Migration helpers — run public or tenant migrations in either mode

Installation

pip install django-tenantkit

Quick Start

1. Create Tenant and Domain models

# tenancy/models.py
from django.db import models
from tenantkit.tenancy.models import TenantMixin, DomainMixin

class Tenant(TenantMixin):
    name = models.CharField(max_length=100)
    auto_create_schema = True

class Domain(DomainMixin):
    pass

2. Configure settings

DATABASES = {
    'default': {
        'ENGINE': 'django_tenants.postgresql_backend',
        'NAME': 'your_db',
        'USER': 'postgres',
        'PASSWORD': 'postgres',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}

INSTALLED_APPS = [
    'django_tenants',
    'tenancy',
    'django.contrib.contenttypes',
    'django.contrib.auth',
    'rest_framework',
    'tenantkit',
    # ... your apps
]

TENANT_DB_MODE = 'schema'          # or 'database'
TENANT_MODEL = 'tenancy.Tenant'
TENANT_DOMAIN_MODEL = 'tenancy.Domain'

TENANT_APPS = ['django.contrib.auth', ...]    # apps in tenant schemas
SHARED_APPS = ['django_tenants', 'tenancy', ...]  # apps in public schema

DATABASE_ROUTERS = [
    'django_tenants.routers.TenantSyncRouter',
    'tenantkit.tenancy.TenantDatabaseRouter',
]

3. Add middleware

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'tenantkit.tenancy.middleware.TenantMiddleware',
    'tenantkit.auth.middleware.AuthMiddleware',
    'tenantkit.auth.middleware.BlockedUserMiddleware',
    # ... rest of middleware
]

4. DRF authentication (optional)

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'tenantkit.auth.TenantAuthentication',
    ],
}

5. Run migrations and create a tenant

python manage.py migrate_schemas --shared
from tenancy.models import Tenant, Domain

public = Tenant(schema_name='public', name='Public')
public.save()
Domain.objects.create(domain='localhost', tenant=public, is_primary=True)

tenant = Tenant(schema_name='acme', name='Acme Corp')
tenant.save()  # auto-creates schema and runs migrations
Domain.objects.create(domain='acme.localhost', tenant=tenant, is_primary=True)

Usage

Request attributes set by AuthMiddleware

request.user_info    # {'id': '1', 'email': '...', 'roles': [...], ...}
request.user_id      # '1'
request.user_email   # 'test@acme.com'
request.user_roles   # ['admin']

# DRF (via TenantAuthentication)
request.user.has_perm('orders.view_order')
request.user.is_superuser  # True if 'superuser' in roles

Permissions

from tenantkit.auth.permissions import IsAuthenticated, IsSuperAdmin

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def protected_view(request): ...

@api_view(['GET'])
@permission_classes([IsSuperAdmin])
def admin_view(request): ...

Path exemptions

TENANT_EXEMPT_PATHS = ['/health/', '/admin/', '/docs/']
AUTH_EXEMPT_PATHS   = ['/health/', '/admin/', '/docs/']
AUTH_OPTIONAL_PATHS = ['/api/catalog/']   # tenant required, auth optional
PUBLIC_GET_ENDPOINTS = ['/api/menu/']     # GET only, no auth, tenant required

Generating tokens (for testing)

import jwt
payload = {'user_id': str(user.id), 'exp': ...}
token = jwt.encode(payload, settings.SECRET_KEY, algorithm='HS256')

Making requests

curl -H "X-Tenant: acme" \
     -H "Authorization: Bearer eyJ..." \
     http://localhost:8000/api/me/

Settings Reference

Tenancy

Setting Default Description
TENANT_DB_MODE 'schema' 'schema' or 'database'
TENANT_MODEL 'tenancy.Tenant' Your Tenant model
TENANT_DOMAIN_MODEL 'tenancy.Domain' Your Domain model
TENANT_HEADER_NAME 'X-Tenant' HTTP header for tenant resolution
REQUIRE_TENANT_BY_DEFAULT True Reject requests without tenant context
TENANT_EXEMPT_PATHS ['/health/', ...] Paths that bypass tenant resolution
TENANT_APPS [] App labels routed to tenant schemas/databases
SHARED_APPS [] App labels routed to public schema / default database
TENANT_DB_NAME_PREFIX 'tenant_' Prefix for database names (database mode)
TENANT_DB_TEMPLATE {} Extra DB config merged with default for tenant databases

Authentication

Setting Default Description
JWT_SECRET_KEY SECRET_KEY Key used to verify JWT signatures
JWT_ALGORITHM 'HS256' JWT signing algorithm
AUTH_CACHE_TIMEOUT 300 Seconds to cache user data (capped to token lifetime)
TENANT_USER_APP_LABEL 'auth' App label of your User model
TENANT_USER_MODEL_NAME 'User' Model name of your User model
REQUIRE_AUTH_BY_DEFAULT True Reject unauthenticated requests
AUTH_EXEMPT_PATHS ['/health/', ...] Paths that bypass authentication
AUTH_OPTIONAL_PATHS [] Paths where auth is optional

Error Responses

All errors return JSON:

{"success": false, "code": "ERROR_CODE", "error": {"message": "...", "hint": "..."}}
Code Status When
MISSING_TENANT_HEADER 400 No X-Tenant and REQUIRE_TENANT_BY_DEFAULT=True
INVALID_TENANT 404 X-Tenant value not found
AUTHENTICATION_REQUIRED 401 No Bearer token
TOKEN_EXPIRED 401 JWT exp in the past
INVALID_SIGNATURE 401 Wrong signing secret
USER_NOT_FOUND 401 user_id not in tenant DB
ACCOUNT_BLOCKED 401 is_blocked=True

Running Tests

pip install pytest
pytest tests/ -v

Requirements

  • Python >= 3.9
  • Django >= 4.2
  • django-tenants >= 3.8
  • Django REST Framework >= 3.14
  • PyJWT >= 2.0
  • PostgreSQL

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_tenantkit-0.1.0.tar.gz (27.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_tenantkit-0.1.0-py3-none-any.whl (25.7 kB view details)

Uploaded Python 3

File details

Details for the file django_tenantkit-0.1.0.tar.gz.

File metadata

  • Download URL: django_tenantkit-0.1.0.tar.gz
  • Upload date:
  • Size: 27.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for django_tenantkit-0.1.0.tar.gz
Algorithm Hash digest
SHA256 5a9d51bee556a3f705fa4f53e1b851c7c708406a8b00a63c44842a0759e892f4
MD5 73d2258bd3f4d649ef15b5e2d5e7ddd3
BLAKE2b-256 5991ce64f5fcbe0339dfb878606b8d78263e751fda9aba316d25858bb97386f2

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_tenantkit-0.1.0.tar.gz:

Publisher: publish.yml on hannan665/django-tenantkit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file django_tenantkit-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for django_tenantkit-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f37e08698f825721129e657bd4ef90e9c2b2bd5bc4494e370ce54aac9f95396a
MD5 2bf38f054f19cc2813d23daeead4efbd
BLAKE2b-256 7aaa9c7317e386757b998cd0195056cc1d4cda2b2e2f88b8f72fddafac79299a

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_tenantkit-0.1.0-py3-none-any.whl:

Publisher: publish.yml on hannan665/django-tenantkit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page