Reputation: 317
My Django application needs to connect an unmanaged (not by Django) external Postgres database.
For testing my project, I need to create the external DB and tables using raw SQL, and delete the DB afterwards.
From reading the Django documentation, one is able to create your own TestRunner class by using the existing DiscoverRunner
class. The setup_test_environment
and teardown_test_environment
methods could be overridden to execute the SQL in order to create the external DB:
from psycopg2 import connect
from django.test.runner import DiscoverRunner
class CustomTestRunner(DiscoverRunner):
"""
Test runner that helps to setup external db if doesn`t exist.
"""
def setup_test_environment(self, *args, **kwargs):
conn = connect(database='postgres', host='localhost', user='my_user', password='password123')
try:
with conn.cursor() as c:
query = """CREATE DATABASE "test_db" """
query += """WITH OWNER = my_user """
query += """ENCODING = 'UTF8' TABLESPACE = pg_default """
query += """LC_COLLATE = 'en_ZA.UTF-8' LC_CTYPE = 'en_ZA.UTF-8' """
query += """CONNECTION LIMIT = -1;"""
c.execute(query)
except Exception as e:
print e
conn.close()
super(CustomTestRunner, self).setup_test_environment(*args,
**kwargs)
def teardown_test_environment(self, *args, **kwargs):
super(CustomTestRunner, self).teardown_test_environment(*args,
**kwargs)
# Delete external database after tests
try:
conn = connect(database='postgres', host='localhost', user='my_user', password='password123')
with conn.cursor() as c:
query = """DROP DATABASE test_db;"""
c.execute(query)
except Exception as e:
print e
conn.close()
conn.close()
However, when I run the tests, I get the following error message:
CREATE DATABASE cannot run inside a transaction block
Is it possible to run and commit SQL within a Django TestRunner? If not, where is the appropriate place to create such an external DB?
Upvotes: 4
Views: 1024
Reputation: 13058
Some database commands such as CREATE DATABASE
can't run within a transaction. You'll need to set the proper isolation level on your database connection:
conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
See http://initd.org/psycopg/docs/extensions.html#isolation-level-constants.
It is also possible to run and commit SQL within a test runner. The more appropriate places to do this would be in the setup_databses
and teardown_databases
methods. Here's an example
def setup_databases(self, **kwargs):
conn = connect(database='postgres', host='localhost', ...)
try:
# set the proper isolation level so CREATE DATABASE works
conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
with conn.cursor() as cur:
# execute SQL in here
query = 'create database TEST_DB'
cur.execute(query)
finally:
conn.close()
return super(CustomTestRunner, self).setup_databases(**kwargs)
The implementation for teardown_databases
should be quite similar.
Upvotes: 1