Installing hstore extension in django nose tests

2019-02-09 19:43发布

问题:

I've installed the hstore extension successfully, and everything works when I syncdb. (I'm using djorm-ext-hstore)

However, nose creates a new temp database to run tests in, and hstore is not installed in it.

I need to run CREATE EXTENSION HSTORE; on the test db right before nose syncs the db, but I can't find any info on how to do that.

Any ideas?

回答1:

This is a non-issue: The best way to fix this is to apply the hstore extension on the default database, template1

psql -d template1 -c 'create extension hstore;'

Reference: How to create a new database with the hstore extension already installed?



回答2:

With Django 1.8:

from django.contrib.postgres.operations import HStoreExtension

class Migration(migrations.Migration):
    ...

    operations = [
        HStoreExtension(),
        ...
    ]

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

EDIT: Just note that there is also a JSONField which handles (un)marshalling of json already and inline search. The HStoreExtension is not necessary for it. Requires Django >=1.11 and Postgres >=9.4.



回答3:

Also you can run sql command in a migration (Django 1.8):

class Migration(migrations.Migration):

    # ...

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


回答4:

I'm assuming you're using django-nose. In this case you should create your own 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

Then in settings.py you should specify your custom test runner:

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


回答5:

Since my last answer, Django deprecated and removed pre_syncdb signal. I've updated the answer to accommodate more recent versions. The basic mechanics are identical for newer versions as both methods rely on signals and the SQL code that only executes if HSTORE extension does not exist.

Django 1.8+

Since Django introduced DB migrations, pre_syncdb signals were marked deprecated in 1.7 and completely removed in 1.9. However, they introduced a new signal called pre_migrate which can be used the same way.

"""
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_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+ (original answer)

My suggestion is to use pre_syncdb signal hook.

See my answer on the other question.

"""
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.

The pre_syncdb signal is fired before the model table is created, making it ideal to ensure the extension is installed when the test database is being set up every time. The IF NOT EXISTS ensures that PostgreSQL ignores the command if the extension is already installed. You'll get an error if you run CREATE EXTENSION on an extension that already exists.

This works for the default Django unit test framework and will most likely work for Django nose tests.

More info on signals: https://docs.djangoproject.com/en/1.6/ref/signals/#management-signals