Code Monkey home page Code Monkey logo

django-multitenant's Introduction

django-multitenant

Python/Django support for distributed multi-tenant databases like Postgres+Citus

Enables easy scale-out by adding the tenant context to your queries, enabling the database (e.g. Citus) to efficiently route queries to the right database node.

There are architecures for building multi-tenant databases viz. Create one database per tenant, Create one schema per tenant and Have all tenants share the same table(s). This library is based on the 3rd design i.e Have all tenants share the same table(s), it assumes that all the tenant relates models/tables have a tenant_id column for representing a tenant.

The following link talks more about the trade-offs on when and how to choose the right architecture for your multi-tenat database:

https://www.citusdata.com/blog/2016/10/03/designing-your-saas-database-for-high-scalability/

Other useful links on multi-tenancy:

  1. https://www.citusdata.com/blog/2017/03/09/multi-tenant-sharding-tutorial/
  2. https://www.citusdata.com/blog/2017/06/02/scaling-complex-sql-transactions/

Installation:

  1. pip install django_multitenant

Supported Django versions/Pre-requisites.

Tested with django 1.10 or higher.

Usage:

Changes in Models:

  1. In whichever files you want to use the library import it by just saying
    import django_multitenant
    from django_multitenant import *
  2. All models should inherit the TenantModel class. Ex: class Product(TenantModel):
  3. Define a static variable named tenant_id and specify the tenant column using this variable. Ex: tenant_id='store_id'
  4. All foreign keys to TenantModel subclasses should use TenantForeignKey in place of models.ForeignKey
  5. A sample model implementing the above 2 steps:
     class Product(TenantModel):
     	store = models.ForeignKey(Store)
     	tenant_id='store_id'
     	name = models.CharField(max_length=255)
     	description = models.TextField()
     	class Meta(object):
     		unique_together = ["id", "store"]
     class Purchase(TenantModel):
       store = models.ForeignKey(Store)
       tenant_id='store_id'
       product_purchased = TenantForeignKey(Product)

Where to Set the Tenant?

  1. Write authentication logic using a middleware which also sets/unsets a tenant for each session/request. This way developers need not worry about setting a tenant on a per view basis. Just set it while authentication and the library would ensure the rest (adding tenant_id filters to the queries). A sample implementation of the above is as follows:
     class SetCurrentTenantFromUser(object):
     	def process_request(self, request):
      	        if not hasattr(self, 'authenticator'):
             		from rest_framework_jwt.authentication import JSONWebTokenAuthentication
             		self.authenticator = JSONWebTokenAuthentication()
                 try:
             		user, _ = self.authenticator.authenticate(request)
                 except:
             		# TODO: handle failure
             		return
                 try:
             		#Assuming your app has a function to get the tenant associated for a user
             		current_tenant = get_tenant_for_user(user)
                 except:
             		# TODO: handle failure
             		return
                 set_current_tenant(current_tenant)
         def process_response(self, request, response):
                 set_current_tenant(None)
                 return response
       MIDDLEWARE_CLASSES = (
     	'our_app.utils.multitenancy.SetCurrentTenantFromUser',
       )
  2. Set the tenant using set_current_tenant(t) api in all the views which you want to be scoped based on tenant. This would scope all the django API calls automatically(without specifying explicit filters) to a single tenant. If the current_tenant is not set, then the default/native API without tenant scoping is used.
     def application_function:
     	# current_tenant can be stored as a SESSION variable when a user logs in.
     	# This should be done by the app
     	t = current_tenant
     	#set the tenant
     	set_current_tenant(t);
     	#Django ORM API calls;
     	#Command 1;
     	#Command 2;
     	#Command 3;
     	#Command 4;
     	#Command 5;

Supported APIs:

  1. Most of the APIs under Model.objects.* except select_related().
  2. Model.save() injects tenant_id for tenant inherited models.
    s=Store.objects.all()[0]
    set_current_tenant(s)
    
    #All the below API calls would add suitable tenant filters.
    #Simple get_queryset()
    Product.objects.get_queryset()
    
    #Simple join
    Purchase.objects.filter(id=1).filter(store__name='The Awesome Store').filter(product__description='All products are awesome')
    
    #Update
    Purchase.objects.filter(id=1).update(id=1)
    
    #Save
    p=Product(8,1,'Awesome Shoe','These shoes are awesome')
    p.save()
    
    #Simple aggregates
    Product.objects.count()
    Product.objects.filter(store__name='The Awesome Store').count()
    
    #Subqueries
    Product.objects.filter(name='Awesome Shoe');
    Purchase.objects.filter(product__in=p);

Credits

This library uses similar logic of setting/getting tenant object as in django-simple-multitenant. We thank the authors for their efforts.

django-multitenant's People

Contributors

begriffs avatar gsgalloway avatar rafax avatar saicitus avatar

Watchers

 avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.