在Django Nose测试中安装hstore扩展

8
我已成功安装了hstore扩展,当我运行syncdb时一切正常。(我正在使用djorm-ext-hstore)
然而,nose会创建一个新的临时数据库来运行测试,但是在其中没有安装hstore。
我需要在nose同步数据库之前在测试数据库上运行CREATE EXTENSION HSTORE;,但我找不到任何相关信息。
有任何想法吗?

1
通过在“template1” postgres 数据库上创建扩展,您可以避免很多麻烦。然后,在此之后创建的任何数据库都将具有 HSTORE 扩展。 - rantanplan
rantaplan,那应该是答案! - 0atman
没关系,我通常避免给出单行回答。很高兴能帮到你。 - rantanplan
当答案只有一行时,它是合法的!非常感谢 :-) - 0atman
6个回答

26

8

使用 Django 1.8(已过时,但仍存在于3.2中):

from django.contrib.postgres.operations import HStoreExtension

class Migration(migrations.Migration):
    ...

    operations = [
        HStoreExtension(),
        ...
    ]

https://docs.djangoproject.com/en/3.2/ref/contrib/postgres/fields/#hstorefield

编辑:请注意,还有一个JSONField可以处理json的(反)编组和内联搜索。HStoreExtension不是必需的。需要Django>=1.11和Postgres>=9.4。


这应该是第一个迁移中的第一个操作 - 在执行任何hstore相关操作的迁移之前。 - Javed
这是一个很棒的答案,唯一的缺点是需要手动编辑/修改迁移(至少在我的情况下)。我想知道为什么在首次使用hstore列时不将此作为初始迁移的一部分来完成呢? - Neil C. Obremski
安装扩展程序需要postgres的超级用户特权。您可以选择在常规Django设置之外安装扩展程序,以避免给予Django系统这些特权。 - Risadinha

4

此外,您可以在迁移中运行 SQL 命令(Django 1.8):

class Migration(migrations.Migration):

    # ...

    operations = [
        migrations.RunSQL('create extension hstore;'),
        # ...

4

我假设您正在使用django-nose。在这种情况下,您应该创建自己的TestSuiteRunner

from django.db import connections, DEFAULT_DB_ALIAS
from django_nose import NoseTestSuiteRunner

class MyTestSuiteRunner(NoseTestSuiteRunner):
    def setup_databases(self):
        result = super(MyTestSuiteRunner, self).setup_databases()

        connection = connections[DEFAULT_DB_ALIAS]
        cursor = connection.cursor()
        cursor.execute('CREATE EXTENSION HSTORE')

        return result

然后在settings.py文件中应指定您的自定义测试运行器:

TEST_RUNNER = 'path.to.my.module.MyTestSuiteRunner'

谢谢!不运行它,我就发现一个问题:如果setup_databases()实际上创建表,那么我们不应该先创建扩展吗? - 0atman
1
我运行时会通知你。 - 0atman
很抱歉,它不起作用。如果我先放置创建扩展,测试数据库就不存在了;如果我将其放在超级用户之后,它永远不会运行,因为错误就发生在那里。 :-/ - 0atman
它也可以与DiscoverRunner(django.test.runner)一起使用。 - matuuar

4
自从我上次回答以来,Django已经弃用并移除了pre_syncdb信号。我已经更新了答案以适应最新版本。对于较新版本,基本机制与旧版相同,因为两种方法都依赖于信号和仅在HSTORE扩展不存在时才执行的SQL代码。
Django 1.8+:
自从Django引入了数据库迁移,pre_syncdb信号已经在1.7中被标记为弃用在1.9中完全删除。然而,他们引入了一个新的信号叫做pre_migrate,可以以同样的方式使用。
"""
This is an example models.py which contains all model definition.
"""
from django.dispatch import receiver
from django.db import connection, models
from django.db.models.signals import pre_migrate
import sys

# The sender kwarg is optional but will be called for every pre_syncdb signal
# if omitted. Specifying it ensures this callback to be called once.
@receiver(pre_migrate, sender=sys.modules[__name__])
def setup_postgres_hstore(sender, **kwargs):
    """
    Always create PostgreSQL HSTORE extension if it doesn't already exist
    on the database before syncing the database.
    Requires PostgreSQL 9.1 or newer.
    """
    cursor = connection.cursor()
    cursor.execute("CREATE EXTENSION IF NOT EXISTS hstore")

# ...rest of your model definition goes here
class Foo(models.Model):
    # ...field definitions, etc.

Django 1.6+(原始答案)

我的建议是使用pre_syncdb信号钩子。

请参见我在另一个问题上的答案。

"""
This is an example models.py which contains all model definition.
"""
from django.dispatch import receiver
from django.db import connection, models
from django.db.models.signals import pre_syncdb
import sys

# The sender kwarg is optional but will be called for every pre_syncdb signal
# if omitted. Specifying it ensures this callback to be called once.
@receiver(pre_syncdb, sender=sys.modules[__name__])
def setup_postgres_hstore(sender, **kwargs):
    """
    Always create PostgreSQL HSTORE extension if it doesn't already exist
    on the database before syncing the database.
    Requires PostgreSQL 9.1 or newer.
    """
    cursor = connection.cursor()
    cursor.execute("CREATE EXTENSION IF NOT EXISTS hstore")

# ...rest of your model definition goes here
class Foo(models.Model):
    # ...field definitions, etc.
pre_syncdb 信号在模型表创建之前触发,因此在测试数据库每次设置时确保安装扩展非常理想。 IF NOT EXISTS 确保如果已经安装了扩展,则 PostgreSQL 忽略该命令。如果在已经存在的扩展上运行 CREATE EXTENSION,则会收到错误提示。
这适用于默认的 Django 单元测试框架,并且很可能适用于 Django nose 测试。
有关信号的更多信息: https://docs.djangoproject.com/en/1.6/ref/signals/#management-signals

1

对我来说这很有效 https://gist.github.com/smcoll/bb2533e4b53ae570e11eb2ab011b887b

from django.db.backends.base import creation
from django.test.runner import DiscoverRunner


class CustomDiscovererRunner(DiscoverRunner):
    def setup_databases(self, **kwargs):
        def wrap_create_test_db(function):
            def decorated_create_test_db(self, verbosity, autoclobber, keepdb):
                test_database_name = function(self, verbosity, autoclobber, keepdb)
                self.connection.close()
                self.connection.settings_dict["NAME"] = test_database_name
                cursor = self.connection.cursor()
                cursor.execute('CREATE EXTENSION IF NOT EXISTS hstore')
                return test_database_name

            return decorated_create_test_db
        creation.BaseDatabaseCreation._create_test_db = wrap_create_test_db(
            creation.BaseDatabaseCreation._create_test_db
        )

        return super(CustomDiscovererRunner, self).setup_databases(**kwargs)

settings.py

TEST_RUNNER = 'my_project.settings_test.CustomDiscovererRunner'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接