[Django]-How to skip creation of one of the test databases in Django

4👍

As requested, here’s what I ended up implementing. A "legacy" database here is a database for which we want to skip creation—Django should assume that it already exists.

You will first need to add this lightly modified test runner, project/runner.py:

from django.test.runner import DiscoverRunner
from django.test.utils import get_unique_databases_and_mirrors
from django.db import connections


class LegacyDatabaseRunner(DiscoverRunner):
    """
    Test runner that will skip attempting to create any database with LEGACY=True
    in its TEST configuration dictionary
    """
    def setup_databases(self, **kwargs):
        return _setup_databases(
            self.verbosity, self.interactive, self.keepdb, self.debug_sql,
            self.parallel, **kwargs
        )


def _setup_databases(verbosity, interactive, keepdb=False, debug_sql=False, parallel=0, **kwargs):
    """Clone of django.test.utils.setup_databases"""
    test_databases, mirrored_aliases = get_unique_databases_and_mirrors()

    old_names = []

    for db_name, aliases in test_databases.values():
        first_alias = None
        for alias in aliases:
            connection = connections[alias]

            # This clause is all that's been added. If the database's TEST configuration
            # has LEGACY=True, skip attempting to create the database, and don't add it
            # to the list of databases to tear down after testing is complete.
            if connection.settings_dict.get('TEST', {}).get('LEGACY', False):
                continue

            old_names.append((connection, db_name, first_alias is None))

            # Actually create the database for the first connection
            if first_alias is None:
                first_alias = alias
                connection.creation.create_test_db(
                    verbosity=verbosity,
                    autoclobber=not interactive,
                    keepdb=keepdb,
                    serialize=connection.settings_dict.get('TEST', {}).get('SERIALIZE', True),
                )
                if parallel > 1:
                    for index in range(parallel):
                        connection.creation.clone_test_db(
                            suffix=str(index + 1),
                            verbosity=verbosity,
                            keepdb=keepdb,
                        )
            # Configure all other connections as mirrors of the first one
            else:
                connections[alias].creation.set_as_test_mirror(connections[first_alias].settings_dict)

    # Configure the test mirrors.
    for alias, mirror_alias in mirrored_aliases.items():
        connections[alias].creation.set_as_test_mirror(
            connections[mirror_alias].settings_dict)

    if debug_sql:
        for alias in connections:
            connections[alias].force_debug_cursor = True

    return old_names

Next, make it the default runner in your project/settings.py:

TEST_RUNNER = 'project.runner.LegacyDatabaseRunner'

Then mark any databases you want to skip creation for with LEGACY=True in your settings:

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql_psycopg2',
            'NAME': ...,
        },
        'legacy_db1': {
            'ENGINE': 'sql_server.pyodbc',
            'NAME': ...,
            'TEST': {
                'LEGACY': True,  # Do not manage this database during tests
            },
        },
    }

Then hopefully running manage.py test works as expected.

Note that this will not work when running parallel tests. I made some progress on that by patching ParallelTestSuite.init_worker and fiddling a bit more with setup_databases, but it’s not fully functional yet.

Old answer, may work in a pinch

This is not a particularly safe way to do it, since the semantics of this configuration option could change, but you can declare one database to be a "replica" of the other:

DATABASES = {
    'default': {
        'NAME': 'transactional',
         ...
    },
    'analytical': {
        'NAME': 'analytical',
        ...
        'TEST': {
            'MIRROR': 'default',
        },
    }
}

The MIRROR configuration option is documented here: https://docs.djangoproject.com/en/dev/topics/testing/advanced/#testing-primary-replica-configurations

Relevant Django source is here: https://github.com/django/django/blob/master/django/test/utils.py

1👍

I didn’t get a solution to ignore the first database creation, but you can reuse an existing database if all migrations have been created on it.

Usually, I execute my Django tests with pytest using the below setup:
enter image description here

Pay attention to the last field: Additional Arguments.

Its value is: --exitfirst --reuse-db. Using pytest with this additional arg also works on the command line, but I prefer PyCham because of Debugger.

Leave a comment