У меня есть проект 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(...)
Возможно ли это? Я не могу найти способ сделать это ...