Правильно ли динамически изменять "django.db.connections.databases" для нескольких баз данных? Или лучше развернуть отдельный API для каждого клиента?

Это мой первый опыт разработки мультитенантного SaaS-приложения на Django, в этом SaaS у каждой компании есть своя собственная база данных PostgreSQL, и эти базы данных создаются динамически при регистрации компании. Я не могу предварительно определить все базы данных в settings.DATABASES, , поскольку компании могут зарегистрироваться в любое время, не требуя перезагрузки сервера.

Мое текущее решение использует Промежуточное программное обеспечение для определения компании по поддомену или токену JWT, а затем изменяет connections.databases во время выполнения чтобы настроить подключение к базе данных компании:

import redis
from django.db import connections
from django.core.exceptions import ImproperlyConfigured
from django.utils.connection import ConnectionDoesNotExist
from rest_framework_simplejwt.authentication import JWTAuthentication
from myapp.models import Company  # Company model stored in the global database

class CompanyDBMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        self.jwt_authenticator = JWTAuthentication()
        self.cache = redis.Redis(host='localhost', port=6379, db=0)

    def __call__(self, request):
        company_db = self.get_database_for_company(request)

        if not company_db:
            raise ImproperlyConfigured("Could not determine the company's database.")

        # Register connection only if it does not exist in `connections.databases`
        if company_db not in connections.databases:
            connections.databases[company_db] = {
                'ENGINE': 'django.db.backends.postgresql',
                'NAME': company_db,
                'USER': 'postgres',
                'PASSWORD': 'your_password',
                'HOST': 'localhost',
                'PORT': '5432',
                'CONN_MAX_AGE': 60,  # To avoid opening and closing connections on each request
            }

        request.company_db = company_db
        response = self.get_response(request)

        # Close connection after the response
        try:
            connections[company_db].close()
        except ConnectionDoesNotExist:
            pass

        return response

    def get_database_for_company(self, request):
        subdomain = request.get_host().split('.')[0]
        company_db = None

        cache_key = f"company_db_{subdomain}"
        company_db = self.cache.get(cache_key)

        if company_db:
            return company_db.decode("utf-8")

        try:
            company = Company.objects.using('default').get(subdomain=subdomain, active=True)
            company_db = company.db_name
            self.cache.setex(cache_key, 300, company_db)  # Cache the database name for 5 minutes
            return company_db
        except Company.DoesNotExist:
            return None

Мои вопросы таковы:

✅ Правильно ли динамически изменять connections.databases при каждом запросе для обработки нескольких баз данных?
✅ Есть ли лучший способ сделать это в Django без перезапуска приложения при регистрации новых баз данных?
✅ Как эта практика влияет на производительность в средах с балансировкой нагрузки и несколькими экземплярами Django?
✅ Было бы лучше развернуть отдельный API для каждого клиента в его собственном контейнере Django?
✅ Я рассматриваю возможность предоставления каждому клиенту его собственного портала в отдельном домене и развертывания его интерфейса только в контейнере, сохраняя при этом централизованный API. Является ли такой подход более эффективным?

Я буду признателен за любые рекомендации по наилучшей практике или потенциальным проблемам, связанным с этим подходом. Спасибо! 😊

В документации Django явно указано, что вам не следует этого делать:

Вам не следует изменять настройки в ваших приложениях во время выполнения. Единственное место, которое вы должны назначить настройкам, - это файл настроек.

Рассмотрите возможность использования маршрутизатора или напишите свой собственный сервер базы данных, расширив django.db.backends.postgresql.

Вы можете использовать библиотеку django-tenants. Она предназначена для этой цели.

https://django-tenants.readthedocs.io/en/latest/

Вернуться на верх