3

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

I get the following:

  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

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.

I'm using Django 1.7 and Python 2.7.6.

Any thoughts or ideas will help.

Thanks!

0

1 Answer 1

7

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;

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)

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),(...)

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

You'll also want to change:

db_table = 'cedirData\".\"persons'

to:

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.

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]

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

Sign up to request clarification or add additional context in comments.

1 Comment

The 'OPTIONS' trick within the settings.py is a great idea, best option IMO

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.