Hello,
We’re trying to run tests in a multi-db project, however we’re having issues where the test migrations runner is using the original DB name, e.g. mydb
instead of the test DB name test_mydb
.
The actual database creation works without issue, and this does not seem to affect the default
database connection.
The Django version is 4.1.10
The rough DATABASE config we’re using is:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'djangodb',
'USER': "...",
'PASSWORD': "...",
'HOST': "...",
"PORT": "...",
'CONN_MAX_AGE': 60,
'OPTIONS': {
'init_command': 'SET default_storage_engine=INNODB; SET sql_mode="STRICT_TRANS_TABLES"',
'read_default_file': 'my.cnf',
}
},
'db_1': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'db_1',
'USER': "...",
'PASSWORD': "...",
'HOST': "...",
"PORT": "...",
'CONN_MAX_AGE': 60,
'OPTIONS': {
'init_command': 'SET default_storage_engine=INNODB; SET sql_mode="STRICT_TRANS_TABLES"',
'read_default_file': 'my.cnf',
}
},
'slave_db_2': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'db_2',
'USER': "...",
'PASSWORD': "...",
'HOST': "...",
"PORT": "...",
'TEST': {
'MIRROR': 'db_2"',
},
'CONN_MAX_AGE': 60,
'OPTIONS': {
'init_command': 'SET default_storage_engine=INNODB; SET sql_mode="STRICT_TRANS_TABLES"',
'read_default_file': 'my.cnf',
}
},
'db_3': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'db_3',
'USER': "...",
'PASSWORD': "...",
'HOST': "...",
"PORT": "...",
'CONN_MAX_AGE': 60,
'OPTIONS': {
'init_command': 'SET default_storage_engine=INNODB; SET sql_mode="STRICT_TRANS_TABLES"',
'read_default_file': 'my.cnf',
}
},
}
We’re also using a custom DB router, incase that has implications:
import threading
import importlib
from django.conf import settings
DATABASE_SLAVES = []
for dbname, params in settings.DATABASES.items():
if not dbname.startswith('slave_'):
continue
DATABASE_SLAVES.append(dbname)
_locals = threading.local()
def slave_switch(value):
_locals.use_slave = value
def using_slave():
return getattr(_locals, 'use_slave', False)
class DBRouter(object):
"""A router to control all database operations"""
def db_for_read(self, model, **hints):
connection = model.connection_name\
if hasattr(model,'connection_name') else 'default'
connection = 'slave_%s' % (connection)\
if using_slave()\
and 'slave_%s' % (connection) in DATABASE_SLAVES\
else connection
return connection
def db_for_write(self, model, **hints):
if hasattr(model,'connection_name'):
return model.connection_name
return None
def allow_relation(self, model1, model2, **hints):
if hasattr(model1,'connection_name')\
and hasattr(model2,'connection_name'):
return model1.connection_name == model2.connection_name
return None
def allow_syncdb(self, db, model):
if hasattr(model,'connection_name'):
return model.connection_name == db
return db == 'default'
def allow_migrate(self, db, app_label, model_name=None, **hints):
if 'model' in hints:
model = hints['model']
if model.__module__ == '__fake__':
try:
module = importlib.import_module('%s.models' % app_label)
model = getattr(module, model._meta.label.split('.')[1])
except:
return db == 'default'
if hasattr(model, 'connection_name'):
return model.connection_name == db
else:
return db == 'default'
return None
We get the following error when running tests when the runner comes to apply migrations, as it’s running against the non-empty development database:
Synchronizing apps without migrations:
Creating tables...
Running deferred SQL...
Running migrations:
Applying mymodels.0002_initial...2024-07-17 11:11:27,646 (p:645964,t:140164725249856) :-: DEBUG - django.db.backends.schema - execute - L:186 - CREATE TABLE `articles` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `headline` varchar(255) NOT NULL, `feedContent` longtext NULL, `pageContent` longtext NULL, `author` varchar(255) NULL, `url` varchar(255) NOT NULL, `pubDate` datetime(6) NULL, `foundDate` datetime(6) NULL, `language` varchar(150) NULL, `duplicateGroupId` bigint NULL); (params None)
Traceback (most recent call last):
File "project/.venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 87, in _execute
return self.cursor.execute(sql)
^^^^^^^^^^^^^^^^^^^^^^^^
File "project/.venv/lib/python3.11/site-packages/django/db/backends/mysql/base.py", line 75, in execute
return self.cursor.execute(query, args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "project/.venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 179, in execute
res = self._query(mogrified_query)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "project/.venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 330, in _query
db.query(q)
File "project/.venv/lib/python3.11/site-packages/MySQLdb/connections.py", line 259, in query
_mysql.connection.query(self, query)
MySQLdb.OperationalError: (1050, "Table 'articles' already exists")
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "project/manage.py", line 22, in <module>
execute_from_command_line(sys.argv)
File "project/.venv/lib/python3.11/site-packages/django/core/management/__init__.py", line 446, in execute_from_command_line
utility.execute()
File "project/.venv/lib/python3.11/site-packages/django/core/management/__init__.py", line 440, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "project/.venv/lib/python3.11/site-packages/django/core/management/commands/test.py", line 24, in run_from_argv
super().run_from_argv(argv)
File "project/.venv/lib/python3.11/site-packages/django/core/management/base.py", line 402, in run_from_argv
self.execute(*args, **cmd_options)
File "project/.venv/lib/python3.11/site-packages/django/core/management/base.py", line 448, in execute
output = self.handle(*args, **options)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "project/.venv/lib/python3.11/site-packages/django/core/management/commands/test.py", line 68, in handle
failures = test_runner.run_tests(test_labels)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "project/.venv/lib/python3.11/site-packages/django/test/runner.py", line 1050, in run_tests
old_config = self.setup_databases(
^^^^^^^^^^^^^^^^^^^^^
File "project/.venv/lib/python3.11/site-packages/django/test/runner.py", line 946, in setup_databases
return _setup_databases(
^^^^^^^^^^^^^^^^^
File "project/.venv/lib/python3.11/site-packages/django/test/utils.py", line 220, in setup_databases
connection.creation.create_test_db(
File "project/.venv/lib/python3.11/site-packages/django/db/backends/base/creation.py", line 78, in create_test_db
call_command(
File "project/.venv/lib/python3.11/site-packages/django/core/management/__init__.py", line 198, in call_command
return command.execute(*args, **defaults)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "project/.venv/lib/python3.11/site-packages/django/core/management/base.py", line 448, in execute
output = self.handle(*args, **options)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "project/.venv/lib/python3.11/site-packages/django/core/management/base.py", line 96, in wrapped
res = handle_func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "project/.venv/lib/python3.11/site-packages/django/core/management/commands/migrate.py", line 349, in handle
post_migrate_state = executor.migrate(
^^^^^^^^^^^^^^^^^
File "project/.venv/lib/python3.11/site-packages/django/db/migrations/executor.py", line 135, in migrate
state = self._migrate_all_forwards(
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "project/.venv/lib/python3.11/site-packages/django/db/migrations/executor.py", line 167, in _migrate_all_forwards
state = self.apply_migration(
^^^^^^^^^^^^^^^^^^^^^
File "project/.venv/lib/python3.11/site-packages/django/db/migrations/executor.py", line 252, in apply_migration
state = migration.apply(state, schema_editor)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "project/.venv/lib/python3.11/site-packages/django/db/migrations/migration.py", line 130, in apply
operation.database_forwards(
File "project/.venv/lib/python3.11/site-packages/django/db/migrations/operations/models.py", line 96, in database_forwards
schema_editor.create_model(model)
File "project/.venv/lib/python3.11/site-packages/django/db/backends/base/schema.py", line 447, in create_model
self.execute(sql, params or None)
File "project/.venv/lib/python3.11/site-packages/django/db/backends/base/schema.py", line 199, in execute
cursor.execute(sql, params)
File "project/.venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 67, in execute
return self._execute_with_wrappers(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "project/.venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers
return executor(sql, params, many, context)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "project/.venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 84, in _execute
with self.db.wrap_database_errors:
File "project/.venv/lib/python3.11/site-packages/django/db/utils.py", line 91, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "project/.venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 87, in _execute
return self.cursor.execute(sql)
^^^^^^^^^^^^^^^^^^^^^^^^
File "project/.venv/lib/python3.11/site-packages/django/db/backends/mysql/base.py", line 75, in execute
return self.cursor.execute(query, args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "project/.venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 179, in execute
res = self._query(mogrified_query)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "project/.venv/lib/python3.11/site-packages/MySQLdb/cursors.py", line 330, in _query
db.query(q)
File "project/.venv/lib/python3.11/site-packages/MySQLdb/connections.py", line 259, in query
_mysql.connection.query(self, query)
django.db.utils.OperationalError: (1050, "Table 'articles' already exists")
Any help would be much appreciated!