9

I am writing a multi-tenant application with python-django.

I want to set database connection based on each request.I thought i could write a middleware where we set the database to be used for that particular database.

import re
from django.db import connections

class SetTenantDatabase(object):
    def process_request(self, request):
        pattern = re.compile("\\b(http://|https://|www.|.com|8000|:|//)\\W\\d+", re.I)
        words = request.get_host()        
        db_name = [pattern.sub("", words)][0].split('.')[0]
        connections.databases['new-alias'] = { 
        'default': {
                    'ENGINE': 'django.db.backends.postgresql_psycopg2',
                    'NAME': 'store1',
                    'USER': 'xxx',
                    'PASSWORD': 'xxx',
                    'HOST': '127.0.0.1',
    } 
                                              }
        conn = connections['new-alias']
        return None

but this is not working.How should i do this.Is the approach wrong or is the solution feasible, and lastly How?

Vijay Shanker
  • 417
  • 4
  • 10

2 Answers2

29

this is the answer, hope it helps someone in future:

import re
import threading 
request_cfg = threading.local()


class RouterMiddleware(object):
    def process_request( self, request):
        pattern = re.compile("\\b(http://|https://|www.|.com|8000|:|//)\\W\\d+", re.I)
        words = request.get_host()        
        db_name = [pattern.sub("", words)][0].split('.')[0]
        request_cfg.cfg = db_name
        return None

    def process_response( self, request, response ):
        if hasattr( request_cfg, 'cfg' ):
            del request_cfg.cfg
        return response


class DatabaseRouter (object):
    def _default_db( self ):
        if hasattr( request_cfg, 'cfg' ):
            return request_cfg.cfg
        else:
            return 'default'

    def db_for_read( self, model, **hints ):
        return self._default_db()

    def db_for_write( self, model, **hints ):
        return self._default_db()

Thanks

Vijay Shanker
  • 417
  • 4
  • 10
  • 4
    if possible could you please add some explanation to your code. It will be helpful to newbies like me – Varun Aug 21 '18 at 06:43
  • Can you explain about threading.Local and how will it manage two simultaneous transactions for different db – Nimish Bansal Sep 04 '18 at 17:35
  • I'll try to provide some explanation for this one. You can have these classes in some file in your app like middlewares.py. Then you add the router middleware to the list 'MIDDLEWARE' in settings.py and the database router in a list called 'DATABASE_ROUTERS'. Once you have those the middleware starts intercepting the requests packets. – Chaitanya Sama Jan 30 '21 at 19:29
  • This too is a great resource with similar approach: https://dzone.com/articles/django-switching-databases – Kaustubh Trivedi Nov 20 '21 at 18:21
1

Maybe you can use:

https://docs.djangoproject.com/en/dev/topics/db/multi-db/#manually-selecting-a-database-for-a-queryset

Entity.objects.using('context1').all()
Entity.objects.using('context2').all()

To select/use a database depending on the request. You can define multiple DBs in the configurartion:

DATABASES = {
    'context1': {
        'NAME': 'context1',
        'ENGINE': 'db.engine.to.use',
        'USER': 'xxx',
        'PASSWORD': 'xxx'
    },
    'context2': {
        'NAME': 'context2',
        'ENGINE': 'db.engine.to.use',
        'USER': 'xxx',
        'PASSWORD': 'xxx'
    }
}