> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sevalla.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Django - Multi-tenancy

> Discover how to set up your Django app for multi-tenancy in Sevalla.

You can enable multi-tenancy in your Django application using the Django-Tenants library, which allows each tenant to have its own subdomain and database schema, keeping data fully isolated.

The following instructions guide you through setting up a Django-Tenants-enabled application on Sevalla. This example assumes you already have a deployed Django app. It is recommended to start with an empty database, as Django-Tenants applies migrations in a tenant-specific manner.

Additionally, a custom domain is required for this setup, since each tenant will use a separate subdomain to implement the multi-tenancy structure.

## Installation and settings

1. Install the django-tenants library and add it to your dependencies.

   ```shellsession theme={null}
   pip install django-tenants
   pip freeze > requirements.txt
   ```
2. Update the database engine to use the Django-Tenants Postgres backend.

   ```python theme={null}
   # settings.py
   DATABASES = {
       'default': env.db(engine="django_tenants.postgresql_backend"), # [!code ++]
   }
   ```
3. A custom database router can then be added to `settings.py`.

   ```python theme={null}
   # settings.py
   DATABASE_ROUTERS = ( # [!code ++:3]
       'django_tenants.routers.TenantSyncRouter',
   )
   ```
4. Add the  `TenantMainMiddleware` to the `MIDDLEWARE` list. It should be added after `CommonMiddleware` to avoid any errors.

   ```python theme={null}
   # settings.py
   MIDDLEWARE = [
       'django.middleware.security.SecurityMiddleware',
       'whitenoise.middleware.WhiteNoiseMiddleware',
       'django.contrib.sessions.middleware.SessionMiddleware',
       'django.middleware.common.CommonMiddleware',
       'django_tenants.middleware.main.TenantMainMiddleware', # [!code ++]
       'django.middleware.csrf.CsrfViewMiddleware',
       'django.contrib.auth.middleware.AuthenticationMiddleware',
       'django.contrib.messages.middleware.MessageMiddleware',
       'django.middleware.clickjacking.XFrameOptionsMiddleware',
   ]
   ```

## App and model

In this example, you’ll create two Django apps. The first, `app`, will contain the models used to manage tenants and domains, along with any views that are publicly accessible. The second app, `dashboard`, will be tenant-specific and will contain functionality and views intended only for individual tenants.

```shellsession theme={null}
python manage.py startapp app
python manage.py startapp dashboard
```

The Client and Domain models must be created in `app/models.py`. The Client model is used to create all tenants. The Domain model is used to associate subdomains with the tenants you create.

```python theme={null}
# app/models.py
from django_tenants.models import TenantMixin, DomainMixin # [!code ++:10]

class Client(TenantMixin):
    name = models.CharField(max_length=100)
    created_on = models.DateField(auto_now_add=True)

    auto_create_schema = True

class Domain(DomainMixin):
    pass
```

## Other settings

Because some apps should be available globally across the project, while others should be isolated per tenant, you’ll need to adjust how `INSTALLED_APPS` is defined.

Start by defining a `SHARED_APPS` list, which replaces the traditional `INSTALLED_APPS` and includes apps that are shared across all tenants. Next, create a `TENANT_APPS` list containing only the apps that should be available to individual tenants. Finally, define `INSTALLED_APPS` as a combination of both lists.

The `SHARED_APPS` list should also include the `django_tenants` app and the main `app` application that manages tenants and domains.

```python theme={null}
SHARED_APPS = [ # [!code ++]
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django_tenants', # [!code ++: 2]
    'app',
]

TENANT_APPS = ["dashboard"] # [!code ++]

INSTALLED_APPS = SHARED_APPS + [app for app in TENANT_APPS if app not in SHARED_APPS] # [!code ++]
```

In `settings.py`, configure Django-Tenants, so that it knows where your core tenant and domain models are defined.

```python theme={null}
TENANT_MODEL = "app.Client" # [!code ++:3]

TENANT_DOMAIN_MODEL = "app.Domain"
```

Finally, in `settings.py`, replace the original `ROOT_URLCONF` with a new configuration pointing to `urls_tenants`, which you’ll create shortly. Then, define `PUBLIC_SCHEMA_URLCONF` to point to your original `urls.py` file, which will continue to handle all public (non-tenant) views.

```python theme={null}
ROOT_URLCONF = 'example.urls_tenants' # [!code ++:2]
PUBLIC_SCHEMA_URLCONF = 'example.urls'
```

## Views

Below are two simple example views that illustrate the difference between public views and tenant-specific views.

### **Public View**

This view lives in the shared `app` application and is accessible across the entire project, regardless of tenant.

```python theme={null}
# app/views.py
from django.http import HttpResponse # [!code ++:4]

def index(request):
    return HttpResponse("Hello, this is the public-specific view!")
```

### **Tenant-Specific View**

This view belongs to the `dashboard` application and is only available within a tenant context. It uses `request.tenant` to access tenant-specific data.

```python theme={null}
# dashboard/views.py
from django.http import HttpResponse # [!code ++:4]

def index(request):
    return HttpResponse(f"Hello, {request.tenant.name}. This is the tenant-specific view!")
```

### **Tenant URL Configuration**

Next, create a `urls_tenants.py` file to define routes that should only be accessible to tenants. This file references views from the tenant-only `dashboard` app.

```python theme={null}
# example/urls_tenants.py
from django.urls import path # [!code ++:7]

from dashboard import views

urlpatterns = [
    path('', views.index, name='index'),
]
```

### **Public URL Configuration**

Finally, keep your public routes in the existing `urls.py` file. These URLs are served from the public schema and remain accessible without a tenant context.

```python theme={null}
# example/urls.py
from django.contrib import admin
from django.urls import path

from app import views # [!code ++]

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.index, name='index'), # [!code ++]
]
```

## Deploying to Sevalla

Before deploying your application, create migrations to create the new models in the database.

```shellsession theme={null}
python manage.py makemigrations
```

[Add a custom start command](https://docs.sevalla.com/applications/processes#edit-the-web-process) to prevent the default migrations from running.

```shellsession theme={null}
gunicorn example.wsgi
```

[Create a job process](https://docs.sevalla.com/applications/processes#add-a-job-process) that runs before deployment that uses the `migrate_schemas` command from Django-Tenants.

```shellsession theme={null}
python manage.py migrate_schemas --shared
```

[Add a custom domain](https://docs.sevalla.com/applications/domains#add-domain) to your project so the subdomains work with django-tenants.

You can now deploy your app in **Deployments** > **Deploy now**.

## Create a Tenant

Once your app is deployed and you can see the public view on your page, add a tenant to the system. The tenant can be added through the [web terminal](https://docs.sevalla.com/applications/web-terminal).

Use the bash web terminal, turn on the virtual environment, and then start a Django shell session.

```shellsession theme={null}
. /opt/venv/bin/activate
python manage.py shell
```

Finally, create tenant and domain objects. Ensure the domain you use here matches the domain associated with your app.

```shellsession theme={null}
tenant = Client.objects.create(schema_name='tenant1', name='Tenant One')
Domain.objects.create(domain='tenant1.your-domain.com', tenant=tenant, is_primary=True)
```

Now you should be able to visit both your base domain and the tenant subdomain and see the public and tenant pages. You can also check your database and see that multiple schemas exist.
