Django всегда обращается к базе данных по умолчанию при создании пользователей
У меня есть проект django с несколькими базами данных:
DATABASES = {
'default': {},
'db1': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / DB1_NAME,
},
'db2': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / DB2_NAME,
}
}
DATABASE_ROUTERS = ['app.router1.Router1', 'app.router2.Router2']
Определен маршрутизатор для db1
:
class Router1:
route_app_labels = {'app', 'sessions', 'auth', 'admin', 'contenttypes'}
def db_for_read(self, model, **hints):
if model._meta.app_label in self.route_app_labels:
return 'db1'
return None
def db_for_write(self, model, **hints):
if model._meta.app_label in self.route_app_labels:
return 'db1'
return None
def allow_relation(self, obj1, obj2, **hints):
if (
obj1._meta.app_label in self.route_app_labels or
obj2._meta.app_label in self.route_app_labels
):
return True
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
if app_label in self.route_app_labels:
return db == 'db1'
return None
<
Все работает хорошо, кроме создания новых пользователей. Когда я пытаюсь создать нового пользователя, я получаю следующую ошибку:
Когда я меняю
db1
наdefault
, создание пользователя работает нормально.Полный отслеживание ошибок:
Traceback (most recent call last): File "/.venv/lib/python3.9/site-packages/django/core/handlers/exception.py", line 55, in inner response = get_response(request) File "/.venv/lib/python3.9/site-packages/django/core/handlers/base.py", line 197, in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) File "/.venv/lib/python3.9/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view return view_func(*args, **kwargs) File "/.venv/lib/python3.9/site-packages/rest_framework/viewsets.py", line 125, in view return self.dispatch(request, *args, **kwargs) File "/.venv/lib/python3.9/site-packages/rest_framework/views.py", line 509, in dispatch response = self.handle_exception(exc) File "/.venv/lib/python3.9/site-packages/rest_framework/views.py", line 469, in handle_exception self.raise_uncaught_exception(exc) File "/.venv/lib/python3.9/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception raise exc File "/.venv/lib/python3.9/site-packages/rest_framework/views.py", line 506, in dispatch response = handler(request, *args, **kwargs) File "/.venv/lib/python3.9/site-packages/rest_framework/mixins.py", line 19, in create self.perform_create(serializer) File "/.venv/lib/python3.9/site-packages/djoser/views.py", line 136, in perform_create user = serializer.save() File "/.venv/lib/python3.9/site-packages/rest_framework/serializers.py", line 212, in save self.instance = self.create(validated_data) File "/.venv/lib/python3.9/site-packages/djoser/serializers.py", line 65, in create user = self.perform_create(validated_data) File "/.venv/lib/python3.9/site-packages/djoser/serializers.py", line 72, in perform_create with transaction.atomic(): File "/.venv/lib/python3.9/site-packages/django/db/transaction.py", line 197, in __enter__ if not connection.get_autocommit(): File "/.venv/lib/python3.9/site-packages/django/db/backends/base/base.py", line 455, in get_autocommit self.ensure_connection() File "/.venv/lib/python3.9/site-packages/django/db/backends/dummy/base.py", line 20, in complain raise ImproperlyConfigured( django.core.exceptions.ImproperlyConfigured: settings.DATABASES is improperly configured. Please supply the ENGINE value. Check settings documentation for more details.
Вернуться на верхКажется, я нашел первопричину этой проблемы. Похоже, что перед сохранением нового пользователя создается транзакция, но эта транзакция всегда пытается получить доступ к базе данных по умолчанию. Если база данных по умолчанию не определена, возвращается вышеуказанная ошибка (в случае, если база данных по умолчанию определена, это означает, что транзакция обращается к базе данных по умолчанию, в то время как пользователи записываются в другую базу данных).
В
djoser\serializers.py
методperform_create
сначала создает атомарную транзакцию без аргументов:def perform_create(self, validated_data): with transaction.atomic(): user = User.objects.create_user(**validated_data) if settings.SEND_ACTIVATION_EMAIL: user.is_active = False user.save(update_fields=["is_active"]) return user
Функция
atomic
создает его с помощьюusing=None
:def atomic(using=None, savepoint=True, durable=False): # Bare decorator: @atomic -- although the first argument is called # `using`, it's actually the function being decorated. if callable(using): return Atomic(DEFAULT_DB_ALIAS, savepoint, durable)(using) # Decorator: @atomic(...) or context manager: with atomic(...): ... else: return Atomic(using, savepoint, durable)
Затем внутри класса
Atomic
в методе__enter__
вызывается функцияget_connection
. Эта функция возвращает базу данныхdefault
в случае, еслиusing
не определена:def get_connection(using=None): """ Get a database connection by name, or the default database connection if no name is provided. This is a private API. """ if using is None: using = DEFAULT_DB_ALIAS return connections[using]
Исправление для этого, вероятно, должно заключаться в передаче права на базу данных в функцию
atomic
. Как упоминалось в вопросе, переименованиеdb1
вdefault
обходит эту проблему.