How to manage tenants in the multitenant app based on django_tenants and saleor.io platform

Bravelab
Bravelab Developers

Read time: 7 min

Aug 04, 2020

Evaluating multi-tenant architecture

Django framework is a mature framework with many packages that can add multitenancy. There are two main points to evaluate when building such a solution: how to store tenants’ data and which package to use.

Storing data for multiple tenants can be achieved in three different ways:

  • create one database per tenant
  • create one schema per tenant
  • have all tenants share the same table(s)

Each solution has its pros and cons. There are many publications that discuss this in detail. Depending on how many tenants we are expecting or how much customization tenants need, we can choose one that suits us best. In our case, we have chosen “one schema per tenant”, which is a sensible middle ground.

Choosing a package requires evaluating:

  • does it support chosen architecture
  • is it production-ready
  • does it have documentation

We have chosen django_tenants package as it meets all our needs. It’s using database schemas to save tenants’ data, is under active development with a stable release cycle, and has comprehensive documentation.

How does it work?

Every tenant has its own schema in the database. There is also one public schema for storing admin and shared apps. Tenants are identified by their hostnames. Multi-tenant middleware analyzes every request and matches the tenant's hostname with their schema. Then it sets a connection to use the tenant’s schema and all queries are run in the tenant context. If no tenant is found, a 404 error is raised.

How to build an admin panel

Writing a custom admin panel can be a daunting task requiring writing a lot of boilerplate code for CRUD operations. Instead, we can use Django to build an admin interface from models. It allows us to concentrate on adding additional functionality. The default tenant model contains only information about schema and domains associated with it. First, we can add fields for storing some more useful information about our tenants like their names, contact information, and activation status.

How to build an admin panel

Writing a custom admin panel can be a daunting task requiring writing a lot of boilerplate code for CRUD operations. Instead, we can use Django to build an admin interface from models. It allows us to concentrate on adding additional functionality. The default tenant model contains only information about schema and domains associated with it. First, we can add fields for storing some more useful information about our tenants like their names, contact information, and activation status.

  
    class Tenant(TenantMixin):
    full_name = models.CharField(max_length=255, default="")
    is_active = models.BooleanField(default=True, blank=True)
    created_on = models.DateField(auto_now_add=True)
    is_ready = models.BooleanField(default=False, blank=True)
    email = models.EmailField(blank=True)
    note = models.TextField(blank=True)
  

Field is_active can be used to temporary disable tenant and block access to their site. Modifying get_tenant method from TenantMiddleware allows us to check if tenant should be available.

  
    from django_tenants.middleware.main import TenantMainMiddleware

class TenantMiddleware(TenantMainMiddleware):
    def get_tenant(self, domain_model, hostname):
        tenant = super().get_tenant(domain_model, hostname)
        if not tenant.is_active:
            raise self.TENANT_NOT_FOUND_EXCEPTION("Tenant is inactive")
        if not tenant.is_ready:
            raise self.TENANT_NOT_FOUND_EXCEPTION("Tenant is not ready")
        return tenant
  

Displaying a list of tenants in the admin panel requires adding two more classes so that domains associated with the tenant appear inside its change form. It is also a good idea to add protection against accidentally removing a public schema that holds all information.

  
    class DomainInline(admin.TabularInline):
    model = Domain
    extra = 0

@admin.register(Tenant)
class TenantAdmin(TenantAdminMixin, admin.ModelAdmin):
list_display = (
        "name",
        "is_active",
        "is_ready",
        "created_on",
    )
inlines = [DomainInline]

def has_delete_permission(self, request, obj=None):
        if obj is not None and obj.schema_name == get_public_schema_name():
            return False
        return super().has_delete_permission(request, obj)
  

With the working admin panel, we can start adding fields that query data from tenant schema. To make it work, we need a way to change schema_name in the connection object. Using context manager from django_tenants.utils allows us to query data for every tenant independently by just passing tenant object, in this case from admin form:

  
    def product_count(self, obj):
    with tenant_context(obj):
        return Product.objects.all().count()
  

The admin panel can be used to show all sorts of information about the performance of every shop. We can query and display the number of available products, orders placed in a given timespan, compute average order amount and spedition costs and so on.

How to manage tenants in the multitenant app based on django_tenants and saleor.io platform

Conclusion

Adding multitenancy to Saleor.io platform is not a complicated task. By choosing the right tools and utilizing the power of the Django admin panel, we are able to create a good starting point for building a multi-tenant ecommerce platform.

Bravelab
Bravelab Developer who is detail-oriented, enthusiastic about data analysis and highly focused on implementing modern solutions to help out business to grow.

Read time: 7 min

Aug 04, 2020

Read more from our brave's writers

Our mission is to be /trusted partner
to our clients/
in the field of webplatform
development & staff augmentation

Check our rank

developmentclutchdevelopment
  • development
  • clutch
Item 1 of 3

0 Professionals

0 Finished project

Kudos.

  • “Not afraid of doing even the most difficult tasks and proposing innovative solutions. They don’t avoid challenges, rely on new technologies, and - most importantly - always carry the projects through from the beginning to the end.”

    Adam Bogdan
    Adam Bogdan Python Developer
Item 1 of 9

Contact.

We're happy to help! Leave your contact data, and we will get in touch with you within one business day.

Tell us about your needs, ask about our experience, portfolio, and even partnership programs.

Contact us

This website stores cookies on your computer. These cookies are used to collect information about how you interact with our website and allow us to remember you. We use this information in order to improve and customize your browsing experience & for analytics & metrics about our visitors both on this website & other media.
To find out more about the cookies we use, see our Privacy Policy.