How to Build Multi Tenants application with Django, Django Rest Framework and Django Tenant


meta-image-3-1024x535 How to Build Multi Tenants application with Django, Django Rest Framework and Django Tenant

What is Multi Tenants Application

In multi-tenant software architecture (also called software multi-tenancy), a single instance of a software application (and its underlying database and hardware) serves multiple tenants (or user accounts). A tenant can be an individual user, but more frequently, it’s a group of users (such as a customer organization) that shares common access to and privileges within the application instance. Each Tenant’s data is isolated from, and invisible to, the other tenants sharing the application instance, ensuring data security and privacy for all tenants.

What-is-Multi-Tenants-Application-1024x490 How to Build Multi Tenants application with Django, Django Rest Framework and Django Tenant

Types of Multi Tenants Modeling

  1. Instance Replication Modeling:- In this modeling, we system spins a new instance for every Tenant. This is easier to start but hard to scale. It becomes challenging when there are more tenants. It’s similar to single-tenant architecture.
  1. Database Segregation Modeling:- In this modeling, we do separate databases for each Tenant to store their data in a specific database. Again this will become hard to handle databases when there is an increase in tenants.
  1. Schema Segregation Modeling:- In this modeling, we use a single database and a single instance of an application. When we create a new tenant, then create a new schema in that database for this Tenant to store their data separately.
Multi-Tenants-modelling-1024x408 How to Build Multi Tenants application with Django, Django Rest Framework and Django Tenant

Note:- In this blog, we will use Schema Segregation Modeling.

Why do we use multi-tenant applications?

Let’s take an example of the Healthcare Domain. In this domain, every client wants to separate and isolate their data from each other. The data in the Healthcare domain is susceptible to patients, so clients want to separate it. In the multi-tenant application, we separate the database or schema so that data will separate. So due to data-related compliance of clients, we are using a multi-tenant application.

What are Schemas?

Generally, a schema can be seen as a directory in an operating system, each directory (schema) with its own set of files (tables and objects). This allows the same table name and objects to be used in different schemas without conflict.

A database contains one or more named schemas, which in turn contain tables. Schemas also contain other named objects, including data types, functions, and operators. The same object name can be used in different schemas without conflict; for example, schema1 and my schema can contain tables named mytable. Unlike databases, schemas are not rigidly separated: a user can access objects in any schemas in the database they are connected to if they have the privileges to do so.

There are several reasons why one might want to use schemas:

  • To allow many users to use one database without interfering with each other.
  • Organize database objects into logical groups to make them more manageable.
  • Third-party applications can be put into separate schemas so they do not collide with the names of other objects.

Schemas are analogous to directories at the operating system level, except that schemas cannot be nested.

For More Information:- https://www.postgresql.org/docs/14/ddl-schemas.html

Requirements

  1. Python (v3.10) – https://www.python.org/
  2. PostgreSQL (v14) – https://www.postgresql.org/
  3. Django (v4.0) – https://www.djangoproject.com/
  4. Django Rest Framework (v3.14.0) – https://www.django-rest-framework.org/
  5. Django Tenants (v3.5.0) – https://django-tenants.readthedocs.io/en/latest/
  • PythonPython is an easy-to-learn, powerful programming language. It has efficient high-level data structures and a simple but effective approach to object-oriented programming.
  • PostgreSQL – PostgreSQL is a powerful, open-source object-relational database system for persisting data.
  • Django – Django is a high-level Python web framework encouraging rapid development and clean, pragmatic design. Built by experienced developers, it takes care of the hassle of web development, so you can focus on writing your app without reinventing the wheel. It’s free and open source.

Installation:- pip install Django==4.0

  • Django Rest Framework – Django REST framework is a powerful and flexible toolkit for building Web APIs.
    Installation:- pip install djangorestframework==3.14.0
  • Django Tenant – Django Tenant is a package; by using this, we will apply multi-tenancy in our application.
    Installation:- pip install Django-tenants==3.5.0

Note:- There is a problem in this package that this package currently supports sub-domain-based multi-tenancy. In addition, this may again make it hard to maintain lots of subdomains

But we will modify this package to multi-tenancy using one single domain.

Notes:-

  • This application enables Django-powered websites to have multiple tenants via PostgreSQL schemas. A vital feature for every Software-as-a-Service website.
  • Django currently provides no simple way to support multiple tenants using the same project instance, even when only the data is different. Because we don’t want you running many copies of your project, you’ll be able to have the following:
  • Multiple customers running on the same instance
    – Shared and Tenant-Specific data
    – Tenant View-Routing (Accept Tenant via Header and then using routing)

Create Multi-Tenancy Application

1. Create a basic Django application

The below command is used to create a Django project with name django_multi_tenancy
Django-admin start project django_multi_tenancy

2. Create an app

The below command is used to create a Django application with the name app
– python manage.py start app

3. Create two model

a. Tenant Model – This model is responsible for storing tenants’ names. You can add more fields if required in your case.
b. Domain Model – This model stores tenants’ domains unique for each Tenant.

From Django.Db import models
from django_tenants.models import DomainMixin, TenantMixin
class Tenant(TenantMixin):
name = models.CharField(max_length=100, unique=True)
#default true, the schema will be automatically created and synced when it is saved
auto_create_schema = True
class Domain(DomainMixin):
Pass

4. Basic Settings

– You’ll have to make the following modifications to your settings.py file.
– This setting is required for application multi-tenancy in application to handle schema-based multi-tenancy.
– Your DATABASE_ENGINE setting needs to be changed to
DATABASES = {
“default”: {
“ENGINE”: “django_tenants.postgresql_backend”,
#..
}
}

5. Add django_tenants.routers.TenantSyncRouter to your DATABASE_ROUTERS setting so that the correct apps can be synced, depending on what’s being synced (shared or Tenant)

This router is responsible for handling routing for a specific tenant. When we change the schema based on the Tenant, then this will handle that situation. We will use routing to switch to a specific tenant schema.
DATABASE_ROUTERS = (
“django_tenants.routers.TenantSyncRouter”,
)

6. Create a middleware.py file and add this code

This is one of the critical files and codes where we will check and verify the Tenant and switch the schema based on the Tenant. When we send the tenant name from Tenant-Header, then in middleware, find that name and validate for the correct tenant name. If this is correct, we change the schema to that specific Tenant. The code below is responsible for this logic.

From django.conf import settings
from django.core.exceptions import DisallowedHost
from django.http import HttpResponseNotFound
from django.db import connection
from django.http import Http404, JsonResponse
from django.urls import set_urlconf
from django.utils.deprecation import MiddlewareMixin
from django_tenants.utils import (
get_public_schema_name,
get_public_schema_urlconf,
get_tenant_types,
has_multi_type_tenants,
remove_www,
)
from app.models import Tenant
class TenantMainMiddleware(MiddlewareMixin):
TENANT_NOT_FOUND_EXCEPTION = Http404
@staticmethod
def hostname_from_request(request):
return remove_www(request.get_host().split(“:”)[0])
def process_request(self, request):
connection.set_schema_to_public()
try:
hostname = self.hostname_from_request(request)
except DisallowedHost:
return HttpResponseNotFound()
#Check the Tenant from headers to change the schema for each request.
tenant_name = request.headers.get(“Tenant-Header”)
try:
tenant = Tenant.objects.get(name__iexact=tenant_name)
except Tenant.DoesNotExist:
if tenant_name != “public”:
return JsonResponse({“detail”: “Tenant not found”}, status=400)
self.no_tenant_found(
request, hostname
) # If no tenant is found, then set to public Tenant and return
return
tenant.domain_url = hostname
request.tenant = tenant
connection.set_tenant(request.tenant)
self.setup_url_routing(request)
def no_tenant_found(self, request, hostname):
if (hasattr(settings, “SHOW_PUBLIC_IF_NO_TENANT_FOUND”) and
settings.SHOW_PUBLIC_IF_NO_TENANT_FOUND):
self.setup_url_routing(request=request, force_public=True)
else:
 raise self.TENANT_NOT_FOUND_EXCEPTION(‘No tenant for hostname “%s”‘
% hostname)

@staticmethod
def setup_url_routing(request, force_public=False):
public_schema_name = get_public_schema_name()
if has_multi_type_tenants():
tenant_types = get_tenant_types()
if not hasattr(request, “tenant”) or (
(force_public or request. Tenant.schema_name ==
get_public_schema_name())
and “URLCONF” in tenant_types[public_schema_name]):
request.urlconf = get_public_schema_urlconf()
else:
tenant_type = request. Tenant.get_tenant_type()
request.urlconf = tenant_types[tenant_type][“URLCONF”]set_urlconf(request.urlconf)
else:
#Do we have a public-specific urlconf?
if hasattr(settings, “PUBLIC_SCHEMA_URLCONF”) and (
force_public or request. Tenant.schema_name ==
get_public_schema_name()
):
request.urlconf = settings.PUBLIC_SCHEMA_URLCONF

Note:- Here, we are using public for the default tenant.

7. Add the middleware app.middleware.TenantMainMiddleware to the top of MIDDLEWARE so that each request can be set to use the correct schema.

This code is responsible for adding the above middleware class into the Django application, so when a request comes, then first, this middleware will call and, according to the tenant schema, will be changed.

MIDDLEWARE = (
“app.middleware.TenantMainMiddleware”,
#…
)

8. Add the context processor django.template.context_processors.request context_processors option of TEMPLATES; otherwise, the Tenant will not be available on request.

The code below is responsible for adding a tenant to the request object so that in our request, we can find the Tenant and set the Tenant in the request.

TEMPLATES = [
{
#…
‘OPTIONS’: {
‘context_processors’: [
‘django.template.context_processors.request’,
#…
],
},
},
]

9. Admin Support (for Django Super Admin)

TenantAdminMixin is available to register the tenant model. The mixin disables save and deletes buttons when not in the current or public Tenant (preventing Exceptions).

from django.contrib import admin
from django_tenants.admin import TenantAdminMixin
from app.models import Domain, Tenant
# Register domain model
admin.site.register(Domain)
# Register tenant model
@admin.register(Tenant)
class TenantAdmin(TenantAdminMixin, admin.ModelAdmin):
list_display = (‘name’, )

10. Create one of our use case models

We will create a sample model that will be Tenant-specific and then verify whether or not our implementation multi-tenancy is applied in our application. I will use this model as TENANT APPS.

From django.db import models
class Hotel(models.Model):
name = models.CharField(max_length=255)
location = models.CharField(max_length=255)
description = models.CharField(max_length=255)
picture = models.ImageField()

11. Configure Tenant and Shared Applications

To use shared and tenant-specific applications, there are two settings called SHARED_APPS and TENANT_APPS. SHARED_APPS is a list of strings like INSTALLED_APPS and should contain all apps you want to be synced to the public. However, if SHARED_APPS is set, these are the only apps syncing to your public schema! The same applies to TENANT_APPS, which expects a tuple of strings where each string is an app. If set, only those applications will be synced to all your tenants. Here’s a sample set. THIRD_PARTY_APPS and default django app will add as SHARED_APPS, and our use case model (Like the Hotel model) is TENANT_APPS

THIRD_PART_APPS = [
# Third party apps
]SHARED_APPS = [
‘django_tenants’,
‘django.contrib.contenttypes’,
‘django.contrib.auth’,
‘django.contrib.sessions’,
‘django.contrib.sites’,
‘django.contrib.messages’,
‘django.contrib.admin’,
] + THIRD_PART_APPS
TENANT_APPS = [
# your tenant-specific apps
‘app.Hotel’,
]
INSTALLED_APPS = SHARED_APPS + [app for app in TENANT_APPS if app not in SHARED_APPS]

12. You also have to set where your tenant & domain models are located.

Below lines are required to specify our tenant and domain model so that Django Tenant will find these models in the application.

TENANT_MODEL = “app.Tenant”

TENANT_DOMAIN_MODEL = “app.Domain”

13. Now run the commands

These commands generate the migration files for Created models and apply those migrations into each database schema.

– python manage.py makemigrations
– python manage.py migrate


Note:- If you use to migrate, then migrations will be applied to both shared and tenant schemas!

14. Create tenants and test multi-tenancy

Step1- Create a superuser
python manage.py createsuperuser

Step2- Log In to the admin panel
http://127.0.0.1:8000/admin/

Step3- Create a Tenant from the admin panel

Step4- Create a domain from the admin panel for that Tenant

I created two tenants, demo1 and demo2

tenants-and-test-multi-tenancy-1024x453 How to Build Multi Tenants application with Django, Django Rest Framework and Django Tenant
tenant-demo-2-1024x449 How to Build Multi Tenants application with Django, Django Rest Framework and Django Tenant

Now your Tenant is created, and also, model migrations are applied. Now you can use your tenant name to call API.

15. Create CRUD API for the Hotel model

# serializers.py file

  • This file is used for creating a Django Rest Framework serializer for API.

    Serializers allow complex data, such as query sets and model instances, to be converted to native Python data types and quickly rendered into JSON, XML, or other content types. Serializers also provide deserialization, allowing parsed data to be converted into complex types after validating the incoming data.

    from rest_framework import serializers
    from app.models import Hotel
    class HotelSerializer(serializers.ModelSerializer):
    class Meta:
    model = Hotel
    fields = “all

# views.py file

  • This file is used for creating view sets for Django Rest Framework API.

    Django REST framework not only allows you to combine the logic for a set of related views in a single class called a viewset. But you may also find conceptually similar implementations like ‘Resources’ or ‘Controllers’ in other frameworks.

    from rest_framework.viewsets import ModelViewSet
    from app.serializer import HotelSerializer
    from app.models import Hotel
    class HotelViewSet(ModelViewSet):
    queryset = Hotel.objects.all()
    serializer_class = HotelSerializer

# urls.py file

  • This file is used for creating URLs for application APIs.

    REST framework supports automatic URL routing to Django and also provides a simple, quick, and consistent way of wiring your view logic to a set of URLs using a router.

    from django.conf import settings
    from rest_framework.routers import DefaultRouter, SimpleRouter
    from app.views import HotelViewSet
    if settings.DEBUG:
    router = DefaultRouter(
    else:
    router = SimpleRouter()
    router.register(“”, HotelViewSet, basename=”hotel-apis”)
    urlpatterns += router.urls

    Note:- Now your API is ready for the test with multi-tenancy

16. Call API with your Tenant (Proof of Concept)

Call-API-with-your-Tenant-1-1024x278 How to Build Multi Tenants application with Django, Django Rest Framework and Django Tenant
Call-API-with-your-Tenant-2 How to Build Multi Tenants application with Django, Django Rest Framework and Django Tenant
  1. Get API

    First, call GET API for both tenants. Both tenants currently no data available in any of the tenants
  1. POST API
postAPI How to Build Multi Tenants application with Django, Django Rest Framework and Django Tenant
postAPI-2 How to Build Multi Tenants application with Django, Django Rest Framework and Django Tenant
  • Let’s create some data in both tenants and verify the data is isolated.
postAPI-3 How to Build Multi Tenants application with Django, Django Rest Framework and Django Tenant
postAPI4 How to Build Multi Tenants application with Django, Django Rest Framework and Django Tenant
  • Let’s verify data inserted in both tenants is isolated.

Note:- Here, the Tenant-Header key is responsible for your tenant name. Make sure you pass the correct tenant name in the Tenant Header. You should pass the Tenant’s name here. If passed public, then this API will use the default schema to manipulate data in the database.

Conclusion

As shown above, we create two tenants in the example, demo1 and demo2. Initially, when we get all the hotels, it returns blank lists. After that, we created two hotels in demo1 Tenant and one Hotel in demo2. After that, when we got the hotels list, in the demo1 Tenant, we were getting two hotels, and in the demo1 Tenant getting one Hotel. Also, if we check the primary key to validate that both are created in different schemas.

We successfully created a multi-tenancy application and tenants and CRUD applications based on the Tenant. With its active development and support, Django-tenant is a valuable tool if you are looking to build scalable, secure, and maintainable multi-tenant applications using Django and Postgres. Following the steps outlined in this article, you can create a scalable, secure, and customizable application that can handle a growing client base with a separate schema for each client.

Kaustubh

I look after Technology at Thinkitive. Interested in Machine Learning, Deep Learning, IoT, TinyML and many more areas of application of machine learning.

Related Articles

58 Comments

  1. An impressive share! I have just forwarded this onto a friend who
    has been doing a little research on this. And he in fact ordered me lunch because I discovered it
    for him… lol. So let me reword this…. Thank YOU
    for the meal!! But yeah, thanx for spending time to talk about
    this issue here on your web site.

  2. I would like to thank you for the efforts you’ve put in writing this site.
    I’m hoping to see the same high-grade blog posts from you later on as well.
    In fact, your creative writing abilities has motivated me to get my very
    own site now ๐Ÿ˜‰

  3. It’s a pity you don’t have a donate button! I’d definitely donate to this brilliant blog!
    I suppose for now i’ll settle for bookmarking and adding your RSS
    feed to my Google account. I look forward to fresh updates and
    will talk about this blog with my Facebook group. Talk soon!

  4. You really make it seem so easy with your
    presentation but I find this topic to be actually something
    that I think I would never understand. It seems
    too complicated and extremely broad for me.
    I’m looking forward for your next post, I will try to get the hang of it!

  5. Hi my loved one! I want to say that this post is amazing, great written and come with approximately all important infos.
    I’d like to see more posts like this .

  6. Hello there! This post couldn’t be written much better!
    Reading through this article reminds me of my previous roommate!
    He continually kept talking about this. I am going to send this information to
    him. Fairly certain he’s going to have a good read. Thank you for sharing!

  7. Write more, thats all I have to say. Literally, it seems as though you relied on the video to
    make your point. You obviously know what youre talking about, why throw away your intelligence on just
    posting videos to your blog when you could be giving us something informative to read?

  8. you are actually a excellent webmaster. The website loading speed
    is amazing. It seems that you are doing any unique trick.

    In addition, The contents are masterwork. you’ve performed
    a wonderful activity in this matter!

  9. Hello! This is my first visit to your blog! We are a collection of
    volunteers and starting a new initiative in a
    community in the same niche. Your blog provided us beneficial information to
    work on. You have done a extraordinary job!

  10. Hey There. I found your blog the use of msn. That is a really neatly written article.
    I’ll be sure to bookmark it and come back to read extra of your helpful information. Thank you for the post.
    I’ll definitely comeback.

  11. Hola! I’ve been reading your web site for a long
    time now and finally got the bravery to go ahead and give you a shout out from
    Houston Tx! Just wanted to mention keep up the excellent job!

  12. Just wish to say your article is as amazing. The clearness in your post is simply spectacular and i could assume you’re
    an expert on this subject. Fine with your permission allow me to grab
    your feed to keep up to date with forthcoming post. Thanks a million and please continue the enjoyable
    work.

  13. Hello there! This article couldn’t be written any better!
    Going through this article reminds me of my previous roommate!
    He continually kept preaching about this. I am going to forward this article to him.

    Fairly certain he’s going to have a very good read.
    Thank you for sharing!

  14. Excellent post. I used to be checking continuously this blog and I’m impressed!
    Very helpful info particularly the final phase ๐Ÿ™‚ I care for such info much.
    I was seeking this certain information for a very lengthy time.
    Thank you and best of luck.

  15. magnificent put up, very informative. I’m wondering why the opposite specialists of this sector do not realize this.
    You should proceed your writing. I’m confident, you
    have a huge readers’ base already!

  16. This is really attention-grabbing, You’re an excessively skilled blogger.
    I have joined your feed and look forward to searching for extra of your fantastic post.

    Additionally, I have shared your website in my
    social networks

  17. It’s in reality a great and helpful piece of information. I am glad that you just shared this helpful info with us.
    Please stay us up to date like this. Thank you for sharing.

  18. My brother recommended I may like this web site. He used to be totally right.
    This put up truly made my day. You cann’t consider simply how a lot time I had spent for this information! Thanks!

  19. I think this is one of the most important info for me.
    And i’m glad reading your article. But wanna remark on few general things, The website style is perfect,
    the articles is really nice : D. Good job, cheers

  20. Hello there! This article couldn’t be written much better!
    Going through this article reminds me of my previous roommate!
    He constantly kept talking about this. I am going to send this article
    to him. Pretty sure he’ll have a great read.

    I appreciate you for sharing!

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button