Multiple mongoDB related to same django rest framework project

815 views Asked by At

We are having one django rest framework (DRF) project which should have multiple databases (mongoDB).Each databases should be independed. We are able to connect to one database, but when we are going to another DB for writing connection is happening but data is storing in DB which is first connected. We changed default DB and everything but no changes.

(Note : Solution should be apt for the usage of serializer. Because we need to use DynamicDocumentSerializer in DRF-mongoengine.

Thanks in advance.

2

There are 2 answers

3
Boris Burkov On

While running connect() just assign an alias for each of your databases and then for each Document specify a db_alias parameter in meta that points to a specific database alias:

settings.py:

from mongoengine import connect


connect(
    alias='user-db',
    db='test',
    username='user',
    password='12345',
    host='mongodb://admin:qwerty@localhost/production'
)

connect(
    alias='book-db'
    db='test',
    username='user',
    password='12345',
    host='mongodb://admin:qwerty@localhost/production'
)

models.py:

from mongoengine import Document


class User(Document):
    name = StringField()

    meta = {'db_alias': 'user-db'}

class Book(Document):
    name = StringField()

    meta = {'db_alias': 'book-db'}
12
Boris Burkov On

I guess, I finally get what you need.

What you could do is write a really simple middleware that maps your url schema to the database:

from mongoengine import *


class DBSwitchMiddleware:
   """
   This middleware is supposed to switch the database depending on request URL.
   """
    def __init__(self, get_response):
        # list all the mongoengine Documents in your project
        import models
        self.documents = [item for in dir(models) if isinstance(item, Document)]

    def __call__(self, request):
        # depending on the URL, switch documents to appropriate database
        if request.path.startswith('/main/project1'):
             for document in self.documents:
                 document.cls._meta['db_alias'] = 'db1'
        elif request.path.startswith('/main/project2'):
             for document in self.documents:
                 document.cls._meta['db_alias'] = 'db2'

        # delegate handling the rest of response to your views
        response = get_response(request)

        return response

Note that this solution might be prone to race conditions. We're modifying a Documents globally here, so if one request was started and then in the middle of its execution a second request is handled by the same python interpreter, it will overwrite document.cls._meta['db_alias'] setting and first request will start writing to the same database, which will break your database horribly.

Same python interpreter is used by 2 request handlers, if you're using multithreading. So with this solution you can't start your server with multiple threads, only with multiple processes.

To address the threading issues, you can use threading.local(). If you prefer context manager approach, there's also a contextvars module.