Можно ли обернуть код Django, чтобы гарантировать, что весь доступ к базе данных для ORM по умолчанию настроен на «использование»? - PullRequest
0 голосов
/ 26 марта 2020

У меня есть проект Django, который обрабатывает соединения с несколькими базами данных. У меня есть Маршрутизатор базы данных, который обрабатывает логи c объектов, которые нужно хранить в определенной c базе данных, которую я легко использую в своих представлениях, поэтому никаких проблем вообще нет.

Например:

class ClientDatabaseRouterMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        return response

    def process_view(self, request, view_func, args, kwargs):
        if 'client_url' in kwargs:
            # kwargs = {key: value.lower() for key, value in kwargs.items()}
            thread_local.client_url = kwargs['client_url'].lower()

        else:
            if hasattr(thread_local, 'client_url'):
                del thread_local.client_url


class ClientDatabaseRouter:
    def _default_db(self):
        from django.conf import settings
        if hasattr(thread_local, 'client_url'):
            db = thread_local.client_url
            if db in settings.DATABASES:
                return db
        else:
            if settings.DEBUG:
                if hasattr(settings, 'DEBUG_CLIENT_DB'):
                    return settings.DEBUG_CLIENT_DB
            return None

    def db_for_read(self, model, **hints):
        instance = hints.pop('instance', None)
        if instance:
            if instance._state.db is not None:
                return instance._state.db

        if model._meta.app_label == 'client':
            return 'clients'
        if model._meta.app_label == 'django_celery_beat':
            return 'default'
        return self._default_db()

    db_for_write = db_for_read

    def allow_relation(self, obj1, obj2, **hints):
        return obj1._state.db == obj2._state.db

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        if db == 'default':
            return app_label == 'django_celery_beat'

        if not app_label == 'client':
            if db == 'clients':
                return False
        else:
            return db == 'clients'

        return None

Однако я также использую Celery и, естественно, храню дорогостоящие операции в этих задачах. Проблема в том, что независимо от того, что я делаю, мне приходится передавать переменную using в мои объекты, так как это отдельный процесс, и он не может использовать мой маршрутизатор: например:

my_thing.objects.using('alias').create(...)

Это не большая проблема, но это очень раздражает для разработки, так как я могу запустить задачу локально без using='', и она работает, но когда я запускаю sh до производства, задача не выполняется если я забуду using='' где-нибудь ... Я предполагаю, что связь с Docker и моей сторонней БД в производственной среде отличается от локальных экземпляров в dev, но в любом случае это раздражает.

В идеале то, что я хочу это некоторый тип блока переноса, который я могу окружить своим кодом задачи в блоке, который заставляет каждую транзакцию с ORM использовать указанный псевдоним c. В моем случае было бы замечательно что-то вроде with transaction.atomic():, например:

with database_alias as 'my_custom_alias':
    my_thing.objects.create(...) 
    # and this essentially acts as my_thing.objects.using('my_custom_alias').create(...)

Возможно ли это? Я не могу найти способ сделать это ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...