Metadata-Version: 2.4
Name: django-multitenant-middleware
Version: 0.1.2
Summary: HTTP + WebSocket middleware for multi-tenant Django projects
Author-email: Vipul Manohar Raut <vipulraut38@gmail.com>
License: MIT License
        
        Copyright (c) 2026 Vipul Manohar Raut
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
Project-URL: Homepage, https://github.com/LazyEpul/django-multitenant-middleware
Keywords: django,multi-tenant,middleware,channels,asgi
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: Django
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: OS Independent
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: Django>=4.2
Requires-Dist: django-tenants>=3.4
Dynamic: license-file

# 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

```bash
pip install django-multitenant-middleware
```

---

## Settings Configuration (HTTP)

1. Define your tenant model in `settings.py`:

```python
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}
```

2. Add the middleware to your Django `MIDDLEWARE` list:

```python
MIDDLEWARE = [
    # Other middleware...
    "django_multitenant_middleware.http_middleware.TenantHTTPMiddleware",
]
```

> The middleware automatically sets `request.tenant` and `request.tenant_context`.

---

## WebSocket Integration (ASGI)

1. In `asgi.py`:

```python
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
```

2. Setup ASGI:

```python
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings")
django.setup()
django_asgi_app = get_asgi_application()
```

3. Configure tenant resolver:

```python
resolver = FlexibleSubdomainTenantResolver(
    base_domain="app.example.com",  # your base domain
    separators=("-", ".")
)
```

4. Wrap WebSocket routes:

```python
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 `BaseTenantResolver` to implement your logic

```python
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:

```python
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_MODEL` points to your tenant model.
* Missing or invalid settings will raise `ImproperlyConfigured`.
* Compatible with Django 4.x+ and Channels 3.x+.
