HTTP + WebSocket middleware for multi-tenant Django projects
Project description
django-multitenant-middleware
django-multitenant-middleware is a Django package that provides multi-tenant support for both HTTP and WebSocket (Channels) requests. It resolves tenants dynamically based on subdomains, hostnames, or custom headers.
Installation
pip install django-multitenant-middleware
Settings Configuration (HTTP)
- Define your tenant model in
settings.py:
CHANNELS_MULTITENANT_TENANT_MODEL = "tenants.Client"
# Optional: Custom resolver class and args
CHANNELS_MULTITENANT_RESOLVER_CLASS = None # defaults to FlexibleSubdomainTenantResolver
CHANNELS_MULTITENANT_RESOLVER_ARGS = {"base_domain": BASE_DOMAIN}
- Add the middleware to your Django
MIDDLEWARElist:
MIDDLEWARE = [
# Other middleware...
"django_multitenant_middleware.http_middleware.TenantHTTPMiddleware",
]
The middleware automatically sets
request.tenantandrequest.tenant_context.
WebSocket Integration (ASGI)
- In
asgi.py:
import os
import django
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from django_multitenant_middleware.ws_middleware import TenantWebSocketMiddleware
from django_multitenant_middleware.resolvers import FlexibleSubdomainTenantResolver
from tenants.models import Client
from core.websocket.routing import websocket_urlpatterns
- Setup ASGI:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings")
django.setup()
django_asgi_app = get_asgi_application()
- Configure tenant resolver:
resolver = FlexibleSubdomainTenantResolver(
base_domain="app.example.com", # your base domain
separators=("-", ".")
)
- Wrap WebSocket routes:
application = ProtocolTypeRouter({
"http": django_asgi_app,
"websocket": TenantWebSocketMiddleware(
AuthMiddlewareStack(
URLRouter(websocket_urlpatterns)
),
tenant_model=Client, # your tenant model
resolver=resolver,
),
})
Tenant Resolvers
-
FlexibleSubdomainTenantResolver: Extracts tenant from subdomain prefixes Example:
tenant1-app.example.com → tenant1 -
HeaderTenantResolver: Extracts tenant from a custom HTTP header Example:
X-Tenant-ID: tenant1 → tenant1 -
Custom Resolver: Subclass
BaseTenantResolverto implement your logic
from channels_multitenant.resolvers import BaseTenantResolver
class MyCustomResolver(BaseTenantResolver):
def resolve(self, scope) -> str | None:
# Implement your custom logic
return "my_tenant"
TenantFetcher (Optional Helper)
TenantFetcher provides async caching for WebSocket tenants:
from channels_multitenant.tenant_fetcher import TenantFetcher
from tenants.models import Client
fetcher = TenantFetcher(Client)
tenant = await fetcher.get_tenant("tenant1")
- Caches tenants (default 5 minutes)
- Async-ready for Channels
Notes
- Ensure
CHANNELS_MULTITENANT_TENANT_MODELpoints to your tenant model. - Missing or invalid settings will raise
ImproperlyConfigured. - Compatible with Django 4.x+ and Channels 3.x+.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_multitenant_middleware-0.1.2.tar.gz.
File metadata
- Download URL: django_multitenant_middleware-0.1.2.tar.gz
- Upload date:
- Size: 7.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.0.1 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ab771ebd6135e8e13667dccd5f3029f0fe7e6ef2ee041a090ad183d1b4a4d3dc
|
|
| MD5 |
fff2d40bfeadb0c28bd81ebda7be4442
|
|
| BLAKE2b-256 |
606995610b56d11ae6c1554282f59339edcf6497557acb6b7c77ef468ea0fb50
|
File details
Details for the file django_multitenant_middleware-0.1.2-py3-none-any.whl.
File metadata
- Download URL: django_multitenant_middleware-0.1.2-py3-none-any.whl
- Upload date:
- Size: 8.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.0.1 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a115ba036fbc5acb5b3066e2c052549af66274832586d16a107435520a4c1a54
|
|
| MD5 |
729169243d743b915cb71336a0529df9
|
|
| BLAKE2b-256 |
12ea0c41f12cc90f337bc1b9deda6e98b041c5ace66f813c15739dd1156499c1
|