Skip to content

Django Tenants URL

Build and Publish Quality Gate Status Security Rating Code Smells Vulnerabilities


Table of Contents


About Django Tenants URL

Django Tenants URL is a wrapper on the top of the django-tenants package that serves a different yet common use case, the multi-tenant implementation via HEADER and not using sub domains.

A special thanks to the team behind Django Tenants.

Dependencies

The project contains views, permissions, models and more addons that can be used across projects.

django-tenants-url is built on the top of the following dependencies:

  1. Django
  2. Django Rest Framework
  3. Django Tenants

Motivation

When implementing multi tenancy architecture there are many factors to consider and cover and those were greatly approached by django-tenants but so summarize, there are 3 common ways:

  1. Shared schemas - The data of all users are shared within the same schema and filtered by common IDs or whatever that is unique to the platform. This is not so great for GDPR.
  2. Shared database, different Schemas - The user's data is split by different schemas but live on the same database.
  3. Different databases - The user's data or any data live on different databases.

As mentioned before, django-tenants-url is a wrapper on the top of django-tenants and therefore we will be approaching the second.

Many companies have limited resources (money, people...) and limited choices from those constraints. When implementing multi tenancy, the default would be to use subdomains in order to access the desired schema. E.g.:

www.mycompany.saascompany.com

My Company is the tenant of the saascompany.com that is publicaly available to the users. When the mycompany is sent to the backend, the middleware splits the subdomain and the TLD (top-level domain) and maps the tenant with the schema associated.

For this work, one of the ways is to change your apache, nginx or any other configurations that accepts and forwards calls to the *.sasscompany.com and performs the above action.

If the frontend and backend are split, extra configurations need also to be made on that front and all of this can be a pain.

What does django-tenants-url solve?

The principle of mapping users to the schemas remains the same but the way of doing it is what diverges from the rest. What if we were able to only use www.sasscompany.com, login as usual and automatically the platform knows exactly to which schema the user needs to be mapped and forward?

This is what django-tenants-url solves. A single url that does the multi tenancy without breaking the principle and architecture and simply using one single url

Installation

Prior to install the django-tenants-url, django-tenants needs to be installed as well. Please follow the installation steps from Django Tenants

After installing django-tenants

Install django-tenants-url

pip install django-tenants-url
  1. The TENANT_MODEL and TENANT_DOMAIN_MODEL from django-tenants need to be also in the settings.py.
  2. Add django-tenants-url to INSTALLED_APPS.
INSTALLED_APPS = [
  ...
  'django_tenants',
  'django_tenants_url',
  ...
]
  1. django-tenants-url offers a special wrapper over the mixins of django-tenants with the same names so you don't need to worry about breaking changes and the additional unique TenantUserMixin that maps the users with a tenant.

  2. Create the models.

# myapp.models.py

from django.db import models
from django_tenants_url.models import TenantMixin, DomainMixin, TenantUserMixin


class Client(TenantMixin):
    """
    This table provides the `tenant_uuid` needed
    to be used in the `X_REQUEST_HEADER` and consumed
    by the RequestUUIDTenantMiddleware.
    """
    pass


class Domain(DomainMixin):
    pass


class TenantUser(TenantUserMixin):
    pass
  1. Add the DTU_TENANT_USER_MODEL to settings.py.
# settings.py

...

DTU_TENANT_USER_MODEL = 'myapp.TenantUser'

...
  1. Update the MIDDLEWARE to have the new RequestUUIDTenantMiddleware. Preferentially at the very top.
# settings.py

...

MIDDLEWARE = [
  'django_tenants_url.middleware.RequestUUIDTenantMiddleware',
  'django.middleware.security.SecurityMiddleware',
  ...
]

...
  1. Generate the migrations.
python manage.py makemigrations
  1. Run the migrations as if it was django-tenants and not the classic migrate.
python manage.py migrate_schemas
  1. The UUID needed for the RequestUUIDTenantMiddleware can be found in your table inherited from the TenantMixin.

None: Do not run python manage.py migrate or else it will sync everything into the public.

And that is it. The RequestUUIDTenantMiddleware should be able to map the TenantUser created with a tenant and route the queries to the associated schema.

Django Tenants URL Settings

# default settings

DTU_TENANT_NAME = "Public"
DTU_TENANT_SCHEMA = "public"
DTU_DOMAIN_NAME = "localhost"
DTU_PAID_UNTIL = "2100-12-31"
DTU_ON_TRIAL = False
DTU_HEADER_NAME = "HTTP_X_REQUEST_ID"
DTU_AUTO_CREATE_SCHEMA = True
DTU_AUTO_DROP_SCHEMA = False
DTU_TENANT_USER_MODEL = None

X_REQUEST_ID

By default django-tenants-url has the header name HTTP_X_REQUEST_ID that will be lookup from the middleware when sent via HTTP. This name can be overriten by the special setting DTU_HEADER_NAME.

Example

A Django Like app implementing Django Tenants Url can be found here.

The example can be found here

License

Copyright (c) 2022-present Tiago Silva and contributors under the MIT license.