# Multitenant Systems - Part II - Building multitenant backend APIs using Django

This is a continuation of part I of the multitenant series. You can [check part 1 here](https://akshaythekkath.hashnode.dev/multitenant-systems-part-i-introduction). In this one, I will discuss how we can build the backend of our app using the third architecture discussed in the part I.

### What are we building?
An online shop app! This will be a multitenant app where a user can create their own online shop and add users and products to this shop. There can exist any number of shops in our app.

![x1.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1620557822414/w7iD-aGl9.png)

A user can create a new shop and visit their shop using the shop code as the subdomain. Let's build the backend APIs for this system using the Django REST Framework.

#### Expected APIs

- Create a shop
- Create a user in this shop
- Create a product in this shop
- List the products in this shop

### Database Design

We will have a table `Tenant` to store all the shops. All the other tables will have a reference to the `Tenant` table.

![database design](https://cdn.hashnode.com/res/hashnode/image/upload/v1620556334016/CjIOru0lY.png)

### Our Django Models

```
import uuid
from django.db import models
from django.contrib.auth.models import AbstractBaseUser

from .managers.tenant import TenantManager
from .managers.user import CustomUserManager


class Tenant(models.Model):

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=128)
    code = models.CharField(max_length=128, unique=True)


class TenantBaseModel(models.Model):

    class Meta:
        abstract = True
    
    objects = TenantManager()
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE)


class User(AbstractBaseUser, TenantBaseModel):

    USERNAME_FIELD = 'id'
    REQUIRED_FIELDS = list()

    user_objects = CustomUserManager()
    first_name = models.CharField(max_length=512)
    last_name = models.CharField(max_length=512, null=True, blank=True)
    email = models.EmailField(max_length=512)

    class Meta:
        unique_together = ('tenant_id', 'email',)


class Product(TenantBaseModel):

    name = models.CharField(max_length=256)
    code = models.CharField(max_length=256)
    added_by = models.ForeignKey(User, on_delete=models.CASCADE)
    added_on = models.DateTimeField(auto_now_add=True)

```

The`Tenant` model on the top stores all the shop information.

The `TenantBaseModel` is an abstract class that can be inherited from all our tenant models like `User` and `Product` so they automatically get the tenant fields.

We have overridden the `objects` in the `TenantBaseModel` with the `TenantManager` which we will see next.

### The TenantManager

```
from django.db import models


class TenantManager(models.Manager):

    def get_queryset(self):
        return None

    def t_filter(self, tenant, **kwargs):
        return super(TenantManager, self) \
            .get_queryset() \
            .filter(tenant=tenant, **kwargs)

```

We override the `get_queryset` to return None. 

So, `AnyTenantModel.objects.all()` or `AnyTenantModel.objects.filter()` will return None.

We can use `t_filter` to get the single tenant related information, `AnyTenantModel.objects.t_filter(tenant=tenant)`.

### Tenant Routing Middleware

Every tenant-based API should have a tenant code passed. We can pass the tenant code via the API headers. This middleware will pick the tenant code from the headers, get the tenant object from the DB, and set the tenant in the request object.

```
from django.http import Http404
from django.urls import reverse

from ..models import Tenant

TENANT_URLS = [
    reverse('tenants-list')
]

class CustomTenantMiddleware:

    def __init__(self, get_response):
        self.get_response = get_response
        self.TENANT_NOT_FOUND_EXCEPTION = Http404

    def __call__(self, request):
        code = request.META.get('HTTP_TENANT_CODE')
        if request.path in TENANT_URLS:
            return self.get_response(request)
        try:
            tenant = Tenant.objects.get(code=code)
            request.tenant = tenant
            return self.get_response(request)
        except Exception:
            raise self.TENANT_NOT_FOUND_EXCEPTION(
                f'Tenant with code \'{code}\' does not exists'
            )
```

### Handling authentication

Once the JWT token authentication is done, we also check if this authenticated user is a part of this tenant.

```
from rest_framework import exceptions
from knox.auth import TokenAuthentication
from ..models import User


class CustomTokenAuthentication(TokenAuthentication):

    def authenticate(self, request):
        user, auth_token = super().authenticate(request)

        if user.tenant != request.tenant:
            raise exceptions.AuthenticationFailed(
                'User does not exist in this tenant')

        return user, auth_token
``` 

### Tenant based viewsets

Once we have completed all the above steps, we can easily integrate all this together. Our authentication class will be the `CustomTokenAuthentication` class, we can set it globally in the settings file as well.

We override `get_queryset` to use `t_filter` and we get the tenant in the `request` object. So `User.objects.t_filter(tenant=self.request.tenant)` means all the users in this tenant.

```
from rest_framework import viewsets
from ..serializers.user import UserSerializer
from ..models import User
from ..authentication.auth import CustomTokenAuthentication


class UserViewSet(viewsets.ModelViewSet):

    serializer_class = UserSerializer
    authentication_classes = (CustomTokenAuthentication, )
    pagination_class = None

    def get_queryset(self):
        return User.objects \
            .t_filter(tenant=self.request.tenant) \
            .order_by('first_name')

```

```
from rest_framework import viewsets
from ..models import Product
from ..serializers.product import ProductSerializer
from ..authentication.auth import CustomTokenAuthentication


class ProductViewSet(viewsets.ModelViewSet):

    serializer_class = ProductSerializer
    authentication_classes = (CustomTokenAuthentication, )
    pagination_class = None

    def get_queryset(self):
        return Product.objects \
            .t_filter(tenant=self.request.tenant) \
            .order_by('code')

```

Tada! You have your multitenant backend APIs ready. [Check out the full code here](https://github.com/akshays94/online-shop-multitenant).

Thanks for reading!
