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.3.tar.gz (27.9 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.3-py3-none-any.whl (25.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: django_tenantkit-0.1.3.tar.gz
  • Upload date:
  • Size: 27.9 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.3.tar.gz
Algorithm Hash digest
SHA256 f89dbb7ee1a78e4c8ebd4ab59486dc9e5ce4c522af567cd5ba16ad02962842bf
MD5 679b84becc4739dca55e7acba6d421d2
BLAKE2b-256 fa4c8a8af996dc9bbcd2a951e624771f7430522f57d654f6949fe29962aab77b

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_tenantkit-0.1.3.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.3-py3-none-any.whl.

File metadata

File hashes

Hashes for django_tenantkit-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 ba7a2527be10309243a0da07d845eb1fcd9ed317eef28fa2cc6f6bba20f74f93
MD5 1237e9dfcb8829de69bab5065131c3b2
BLAKE2b-256 11150a9580812a1e1a7ea0845e6c184e907b1cb3bd0165fc6867554582e07629

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_tenantkit-0.1.3-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