【问题标题】:How to skip creation of one of the test databases in Django如何跳过在 Django 中创建测试数据库之一
【发布时间】:2018-12-06 20:31:58
【问题描述】:

我们在 Django 应用程序中使用了两个数据库,一个是“事务性”数据库,一个是“分析性”数据库(用作数据仓库)。 “事务”模型是使用 django 模型创建的,而“分析”模型是使用我们的自定义脚本创建的,其中包含“事务”表的多个连接的原始 sql。

当我们为 django 应用程序运行测试时,两个数据库都会创建它们的测试对应版本,比如“test_transactional”和“test_analytical”。 “test_transactional”的创建是可以的,但我们希望跳过“test_analytical”的创建,因为它将由我们的自定义脚本创建和填充。

来自詹金斯日志的片段:

python manage.py test --keepdb cis.tests.test_views --  
noinput --settings=strainprint.settings.local --verbosity=2
...
Using existing test database for alias 'analytics' 
('test_strainprint_analytics')...
...
Synchronizing apps without migrations:
Creating tables...
Creating table django_admin_log
Creating table auth_permission
...

有没有办法在 Django 中实现这一点?我们正在使用 django 1.10。

【问题讨论】:

    标签: python django database-migration


    【解决方案1】:

    根据要求,这是我最终实施的。这里的“遗留”数据库是我们想要跳过创建的数据库——Django 应该假设它已经存在。

    您首先需要添加这个经过轻微修改的测试运行程序,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
    

    接下来,将其设为project/settings.py 中的默认运行器:

    TEST_RUNNER = 'project.runner.LegacyDatabaseRunner'
    

    然后在您的设置中使用LEGACY=True 标记您想要跳过创建的任何数据库:

        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
                },
            },
        }
    

    然后希望运行 manage.py test 可以按预期工作。

    请注意,在运行并行测试时,这将起作用。我通过修补ParallelTestSuite.init_worker 并在setup_databases 上进行了一些修改,在这方面取得了一些进展,但它还没有完全发挥作用。

    旧答案,可能会在紧要关头工作

    这不是一种特别安全的方法,因为此配置选项的语义可能会改变,但您可以将一个数据库声明为另一个数据库的“副本”:

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

    MIRROR 配置选项记录在此:https://docs.djangoproject.com/en/dev/topics/testing/advanced/#testing-primary-replica-configurations

    相关的 Django 源码在这里:https://github.com/django/django/blob/master/django/test/utils.py

    【讨论】:

    • 我完全忘记了这个问题,因为我学会了忍受 5 分钟的测试设置。所以出于某种原因,我现在尝试使用它,但问题出在这部分(来自文档):配置测试环境时,不会创建副本的测试版本。相反,与副本的连接将被重定向到默认点。 这是一个问题,因为我应该转到分析数据库的查询将被重定向到没有分析表的事务性查询。
    • 我最终不得不创建一个自定义测试运行器。它允许我在我的 Django 设置中将数据库标记为“旧版”,并且在测试设置期间,运行程序将简单地跳过任何以这种方式标记的数据库。如果对您有用,我可以在某处分享实现。
    • 是的,这会有所帮助。也许你可以编辑你的答案?
    • 如果底层数据库不同(MySQL、postgres),镜像选项不起作用
    • 这个答案应该被标记为最佳答案!
    猜你喜欢
    • 2015-12-15
    • 2013-05-25
    • 2020-11-17
    • 2012-05-20
    • 2012-04-20
    • 1970-01-01
    • 1970-01-01
    • 2020-09-07
    • 2023-03-28
    相关资源
    最近更新 更多