#30541: Django MultiDB tests not loading fixtures as expected
---------------------------------------------+------------------------
Reporter: Vackar Afzal | Owner: nobody
Type: Bug | Status: new
Component: Testing framework | Version: 2.2
Severity: Normal | Keywords:
Triage Stage: Unreviewed | Has patch: 0
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
---------------------------------------------+------------------------
I've recently upgraded from Django 2.0 to Django 2.2 and have found the
fixture loading logic appears to have changed. The core issue seems to be
related to the introduction of `databases`
Given the following test case:
{{{
class BaseTestCase(TestCase, TestUtilsMixin):
databases = '__all__'
fixtures = [
'data_x1.default.yaml',
'data_x2.default.yaml',
'data_x3.default.yaml',
'data_x4.default.yaml',
'data_x5.default.yaml',
'data_x6.default.yaml'
]
}}}
I would expect data_xx fixtures to only to be loaded into the 'default'
alias, but it appears to be loading into all connections defined in
`DATABASES`, resulting in the following error
{{{
Error
Traceback (most recent call last):
File "django/db/backends/utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
File "django/db/backends/oracle/base.py", line 510, in execute
return self.cursor.execute(query, self._param_generator(params))
cx_Oracle.DatabaseError: ORA-00942: table or view does not exist
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "django/core/serializers/pyyaml.py", line 73, in Deserializer
yield from PythonDeserializer(yaml.load(stream, Loader=SafeLoader),
**options)
File "django/core/serializers/python.py", line 147, in Deserializer
obj = base.build_instance(Model, data, using)
File "django/core/serializers/base.py", line 266, in build_instance
default_manager.db_manager(db).get_by_natural_key(*natural_key).pk
File "managers.py", line 15, in get_by_natural_key
return self.get(name=name)
File "django/db/models/manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "django/db/models/query.py", line 402, in get
num = len(clone)
File "django/db/models/query.py", line 256, in __len__
self._fetch_all()
File "django/db/models/query.py", line 1242, in _fetch_all
self._result_cache = list(self._iterable_class(self))
File "django/db/models/query.py", line 55, in __iter__
results = compiler.execute_sql(chunked_fetch=self.chunked_fetch,
chunk_size=self.chunk_size)
File "django/db/models/sql/compiler.py", line 1100, in execute_sql
cursor.execute(sql, params)
File "raven/contrib/django/client.py", line 127, in execute
return real_execute(self, sql, params)
File "django/db/backends/utils.py", line 67, in execute
return self._execute_with_wrappers(sql, params, many=False,
executor=self._execute)
File "django/db/backends/utils.py", line 76, in _execute_with_wrappers
return executor(sql, params, many, context)
File "django/db/backends/utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
File "django/db/utils.py", line 89, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "django/db/backends/utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
File "django/db/backends/oracle/base.py", line 510, in execute
return self.cursor.execute(query, self._param_generator(params))
django.db.utils.DatabaseError: ORA-00942: table or view does not exist
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "django/test/testcases.py", line 1131, in setUpClass
call_command('loaddata', *cls.fixtures, **{'verbosity': 0, 'database':
db_name})
File "django/core/management/__init__.py", line 148, in call_command
return command.execute(*args, **defaults)
File "django/core/management/base.py", line 364, in execute
output = self.handle(*args, **options)
File "django/core/management/commands/loaddata.py", line 72, in handle
self.loaddata(fixture_labels)
File "django/core/management/commands/loaddata.py", line 114, in
loaddata
self.load_label(fixture_label)
File "django/core/management/commands/loaddata.py", line 172, in
load_label
for obj in objects:
File "django/core/serializers/pyyaml.py", line 77, in Deserializer
raise DeserializationError() from exc
}}}
I've hacked together a workaround by overriding setUpClass as follows:
{{{
@classmethod
def setUpClass(cls):
if not cls._databases_support_transactions():
return
cls.cls_atomics = cls._enter_atomics()
if cls.fixtures:
for db_name in cls._databases_names(include_mirrors=False):
for fixture in cls.fixtures:
load_data = True
fixture_sections = fixture.split('.')
# Very naive hack to see if a connection alias is in
the fixture
if len(fixture_sections) == 3 and fixture_sections[1]
!= db_name:
load_data = False
if load_data:
try:
call_command('loaddata', fixture,
**{'verbosity': 0, 'database': db_name})
except Exception:
cls._rollback_atomics(cls.cls_atomics)
cls._remove_databases_failures()
raise
try:
cls.setUpTestData()
except Exception:
cls._rollback_atomics(cls.cls_atomics)
cls._remove_databases_failures()
raise
}}}
But this has it's own issues. If I use `databases = '__all__'` this error
is thrown
{{{
Error
Traceback (most recent call last):
File "/Users/vafzal/anaconda3/envs/centaur/lib/python3.7/site-
packages/django/db/utils.py", line 166, in ensure_defaults
conn = self.databases[alias]
KeyError: '_'
}}}
If I use `databases = {'__all__'}` this error is thrown
{{{
Error
Traceback (most recent call last):
File "/Users/vafzal/anaconda3/envs/centaur/lib/python3.7/site-
packages/django/db/utils.py", line 166, in ensure_defaults
conn = self.databases[alias]
KeyError: '__all__'
}}}
Instead, I have to use:
{{{
databases = {'default', 'other_conn', ...}
}}}
But unless I list all connections in DATABASES I get this error:
{{{
Error
Traceback (most recent call last):
File "tests/base.py", line 253, in tearDownClass
cls._remove_databases_failures()
File "django/test/testcases.py", line 240, in _remove_databases_failures
setattr(connection, name, method.wrapped)
AttributeError: 'function' object has no attribute 'wrapped'
}}}
The least hacky solution I've found to this problem is to do this:
{{{
class BaseTestCase(TestCase):
databases = '__all__'
default_fixtures = [
'data_x1.default.yaml',
'data_x2.default.yaml',
'data_x3.default.yaml',
'data_x4.default.yaml',
'data_x5.default.yaml',
'data_x6.default.yaml'
]
@classmethod
def setUpClass(cls):
super().setUpClass()
call_command('loaddata', *cls.default_fixtures, **{'verbosity': 0,
'database': 'default'})
}}}
Is this a bug, or am I simply not initialising the tests correctly?
--
Ticket URL: <https://code.djangoproject.com/ticket/30541>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/054.9763a046d99314c5e03fe4f5995619f7%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.