Django аутентифицирует несколько баз данных
Я переписываю устаревшее приложение, в котором есть база данных для каждого клиента. Каждый клиент имеет свою собственную аутентификацию и пользовательский набор. Таким образом, мне понадобится пользовательский сервер аутентификации, потому что для аутентификации django используется только значение по умолчанию. Я написал промежуточное программное обеспечение, которое проверяет URL-адрес при каждом запросе и извлекает из него информацию, чтобы установить имя_базы_данных по запросу.
Если бы у меня был доступ к запросу во время обработки моего пользовательского интерфейса аутентификации, я мог бы легко выполнять вызовы базы данных как user = User.objects.using(request.db).get(username=username)
Однако я не вижу простого способа сделать это. Я видел ответ на этот вопрос здесь: Получите доступ к request.session из backend.get_user, но это не выглядит поточно- ориентированным, поэтому я не хочу идти по этому пути.
Единственное решение, которое, как я вижу, по-прежнему использует django-auth, - это создать сервер аутентификации для каждого клиента, который задает имя базы данных для использования в качестве атрибута класса. Затем я создал бы пользовательскую функцию входа в систему, которая устанавливает request.session['_auth_user_backend'] в качестве пользовательского бэкенда. Таким образом, когда get_user(user_id) вызывается при каждом запросе, он использует клиентский бэкэнд, который знает, из какой базы данных запрашивать.
Я хотел бы избежать управления бэкэндом аутентификации для каждого клиента, если это возможно. Есть лучший способ сделать это?
2 ответа
Так как auth backend не вызывает метод QuerySet using
Вы можете использовать маршрутизатор базы данных с локальной переменной потока и некоторое промежуточное программное обеспечение, чтобы установить для переменной имя базы данных клиента. Промежуточное программное обеспечение должно быть размещено до промежуточного программного обеспечения аутентификации.
Локальная переменная потока является потокобезопасной. Это создает локальную глобальную переменную потока.
Если бы вы следовали пути запроса, он сделал бы следующее:
- Запрос хиты Джанго
- Ваше пользовательское промежуточное ПО извлекает имя базы данных из URL и устанавливает его в локальную глобальную переменную потока.
Промежуточное программное обеспечение для аутентификации django запускается и устанавливает пользователя, выполняя запрос
User.object.get(id=user_id)
, Это будет использовать ваш маршрутизатор базы данных, который будет просто возвращать локальную глобальную переменную потока, которая была установлена в предыдущем промежуточном программном обеспечении.Запрос продолжается в остальной части стека Django.
Например, у вас есть следующие модули:
my_app / middleware.py
from threading import local
my_local_global = local()
class CustomerMiddleware(object):
def process_request(self, request):
my_local_global.database_name = get_database_name(request)
my_app / routers.py
from middleware import my_local_global
class MultiCustomerRouter(object):
def db_for_read(self, model, **hints):
return my_local_global.database_name
settings.py
...
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'my_app.middleware.CustomerMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
)
DATABASE_ROUTERS = ['my_app.routers.MultiCustomerRouter']
...
Вероятно, вы можете использовать маршрутизаторы базы данных Django для управления этим. Если вы знаете, какой пользователь обращается к какой базе данных, вы можете просто определить ее на основе логики для пользовательской модели.