多个 Postgres 模式的 Django 单元测试失败 [英] Django unit test failing for multiple Postgres schemas

查看:43
本文介绍了多个 Postgres 模式的 Django 单元测试失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的 Postgres 数据库有 3 个模式:default、cedirData 和 webData.

My Postgres DB has 3 schemas: default, cedirData and webData.

对于那些指向与默认模式不同的模式的模型,我将其指定如下:

For those models that are pointing to a different schema than default, I'm specifying this as follows:

class Person(models.Model):
    first_name = models.CharField(max_length=200, null=False, blank=False)
    last_name = models.CharField(max_length=200, null=False, blank=False)

    class Meta:
        db_table = 'cedirData\".\"persons'

应用程序运行良好,但是当我尝试运行测试时:

The application works just fine, but when I try to run the tests:

$ ./manage.py test

我得到以下信息:

  File "/home/wbrunetti/.virtualenvs/cedir/local/lib/python2.7/site-packages/django/core/management/commands/migrate.py", line 160, in handle
    executor.migrate(targets, plan, fake=options.get("fake", False))
  File "/home/wbrunetti/.virtualenvs/cedir/local/lib/python2.7/site-packages/django/db/migrations/executor.py", line 63, in migrate
    self.apply_migration(migration, fake=fake)
  File "/home/wbrunetti/.virtualenvs/cedir/local/lib/python2.7/site-packages/django/db/migrations/executor.py", line 97, in apply_migration
    migration.apply(project_state, schema_editor)
  File "/home/wbrunetti/.virtualenvs/cedir/local/lib/python2.7/site-packages/django/db/migrations/migration.py", line 107, in apply
    operation.database_forwards(self.app_label, schema_editor, project_state, new_state)
  File "/home/wbrunetti/.virtualenvs/cedir/local/lib/python2.7/site-packages/django/db/migrations/operations/models.py", line 36, in database_forwards
    schema_editor.create_model(model)
  File "/home/wbrunetti/.virtualenvs/cedir/local/lib/python2.7/site-packages/django/db/backends/schema.py", line 270, in create_model
    self.execute(sql, params)
  File "/home/wbrunetti/.virtualenvs/cedir/local/lib/python2.7/site-packages/django/db/backends/schema.py", line 98, in execute
    cursor.execute(sql, params)
  File "/home/wbrunetti/.virtualenvs/cedir/local/lib/python2.7/site-packages/django/db/backends/utils.py", line 65, in execute
    return self.cursor.execute(sql, params)
  File "/home/wbrunetti/.virtualenvs/cedir/local/lib/python2.7/site-packages/django/db/utils.py", line 94, in __exit__
    six.reraise(dj_exc_type, dj_exc_value, traceback)
  File "/home/wbrunetti/.virtualenvs/cedir/local/lib/python2.7/site-packages/django/db/backends/utils.py", line 65, in execute
    return self.cursor.execute(sql, params)
django.db.utils.ProgrammingError: schema "cedirData" does not exist

看起来可能与迁移有关.由于数据库表已经存在,我只是创建了初始迁移并运行了 --fake:

Looks like it might have something to do with the migrations. Since the DB tables already existed I just created the initial migrations and ran a --fake:

$ python manage.py makemigrations
$ ./manage.py migrate --fake

测试数据库仅使用默认架构创建.

The test DB is created with default schema only.

我使用的是 Django 1.7 和 Python 2.7.6.

I'm using Django 1.7 and Python 2.7.6.

任何想法或想法都会有所帮助.

Any thoughts or ideas will help.

谢谢!

推荐答案

许多其他数据库引擎不使用架构.通过在模型中指定架构,您在 postgres 代码中引入了依赖项.

Schemas aren't used in many other DB engines. By specifying a schema in your models, you've introduced a dependency in your code for postgres.

有两种方法可以解决您的问题;

There are two routes you can take to solve your problem;

首先,您可以为 postgres 用户添加默认搜索路径.这种方法的缺点是模式不能再用于命名空间,但优点是如果您的数据库更改为不同的引擎,您的代码将正常运行.命名表可以通过选择一些标准的表命名方式来实现,类似于 Django 默认的命名方式(例如 appName_className)

First, you could add a default search path to your postgres user. The disadvantage of this approach is that schemas can no longer be used for namespacing, but the advantage is that if your database ever changes to a different engine, your code will function just fine. Namespacing your tables can be achieved by choosing some standard way of naming your tables, similar to the way that Django does it by default (e.g. appName_className)

有两种方法可以实现这一点.这样做的 postgres 命令是:

There are two ways to achieve this. The postgres command to do it this way is:

ALTER USER (your user) SET search_path = "$user",(schema1),(schema2),(schema3),(...)

django 唯一的方法是:

The django-only way to do it would be:

# Warning! This is untested, I just glanced at the docs and it looks right.
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        # some configuration here
        'OPTIONS': {
            'options': '-c search_path=schema1,schema2,schema3'
        }
    }
}

你还想改变:

db_table = 'cedirData\".\"persons'

到:

db_table = 'persons'

作为奖励,您现在可以使用:

As a bonus, you can now use:

manage.py inspectdb > models.py

这是一个不错的功能,这样您就不必手动复制现有数据库.

which is a nice feature, that way you don't have to copy your existing database by hand.

然而,如果模式命名空间在您的数据库中大量使用并且其他应用程序依赖它,则此解决方案将无济于事.另一种方法是编写一个自定义测试运行程序来在您的测试数据库中创建这些模式.这比上述方法更复杂,并且可能有点混乱.我真的不建议这样做,但如果您有兴趣,我可以尝试提供帮助.

This solution will not help you however, if schema namespacing was used heavily on your database and other applications rely on it. A different approach would be to write a custom testrunner to create those schemas in your test database. This is somewhat more involved than the above approach, and can be kind of messy. I don't really recommend doing this, but if you're interested I could try to help.

一种不那么凌乱但更hacky"的方法是在运行测试时简单地覆盖元数据.这也将是一个测试运行器.

A less messy, but more 'hacky' way would be to simply override meta when tests are being run. This would also be a testrunner.

from django.test.simple import DjangoTestSuiteRunner
from django.db.models.loading import get_models

class SchemaModelTestRunner(DjangoTestSuiteRunner):
    """Docstring"""
    def setup_test_environment(self, *args, **kwargs):
        self.original_db_tables = {}
        self.schema_models = [m for m in get_models()
                                 if '"."' in m._meta.db_table]
        for m in self.schema_models:
            schema, table = m._meta.db_table.split('"."')
            self.original_db_tables[m] = m._meta.db_table
            m._meta.db_table = 'schema_'+schema+'_table_'+table

        super(SchemaModelTestRunner, self).setup_test_environment(*args,
                                                                   **kwargs)
    def teardown_test_environment(self, *args, **kwargs):
        super(SchemaModelTestRunner, self).teardown_test_environment(*args,
                                                                      **kwargs)
        # reset models
        for m in self.schema_models:
            m._meta.db_table = self.original_db_tables[m]

您还需要在 settings.py 文件中将其定义为 testrunner.

You'll also want to define this as a testrunner in your settings.py file.

这篇关于多个 Postgres 模式的 Django 单元测试失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆