summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Jerdonek <chris.jerdonek@gmail.com>2016-08-15 07:10:40 -0700
committerTim Graham <timograham@gmail.com>2016-08-23 15:08:20 -0400
commita3db480393d0065fc69834057f0e02a4afc65df9 (patch)
tree5595ed9baf8a4a60e3a8c16707f7690fda55c5da
parentbc1e2d8e8edde6cc7d2657c68242a13ee65a15b8 (diff)
Fixed #27061 -- Added a TEST['TEMPLATE'] setting for PostgreSQL.
-rw-r--r--django/db/backends/postgresql/creation.py31
-rw-r--r--docs/ref/databases.txt10
-rw-r--r--docs/ref/settings.txt14
-rw-r--r--docs/releases/1.11.txt3
-rw-r--r--tests/backends/test_creation.py50
5 files changed, 98 insertions, 10 deletions
diff --git a/django/db/backends/postgresql/creation.py b/django/db/backends/postgresql/creation.py
index 3efcfc7692..2f26b14e52 100644
--- a/django/db/backends/postgresql/creation.py
+++ b/django/db/backends/postgresql/creation.py
@@ -5,28 +5,42 @@ from django.db.backends.base.creation import BaseDatabaseCreation
class DatabaseCreation(BaseDatabaseCreation):
+ def _quote_name(self, name):
+ return self.connection.ops.quote_name(name)
+
+ def _get_database_create_suffix(self, encoding=None, template=None):
+ suffix = ""
+ if encoding:
+ suffix += " ENCODING '{}'".format(encoding)
+ if template:
+ suffix += " TEMPLATE {}".format(self._quote_name(template))
+ if suffix:
+ suffix = "WITH" + suffix
+ return suffix
+
def sql_table_creation_suffix(self):
test_settings = self.connection.settings_dict['TEST']
assert test_settings['COLLATION'] is None, (
"PostgreSQL does not support collation setting at database creation time."
)
- if test_settings['CHARSET']:
- return "WITH ENCODING '%s'" % test_settings['CHARSET']
- return ''
+ return self._get_database_create_suffix(
+ encoding=test_settings['CHARSET'],
+ template=test_settings.get('TEMPLATE'),
+ )
def _clone_test_db(self, number, verbosity, keepdb=False):
# CREATE DATABASE ... WITH TEMPLATE ... requires closing connections
# to the template database.
self.connection.close()
- qn = self.connection.ops.quote_name
source_database_name = self.connection.settings_dict['NAME']
target_database_name = self.get_test_db_clone_settings(number)['NAME']
+ suffix = self._get_database_create_suffix(template=source_database_name)
+ creation_sql = "CREATE DATABASE {} {}".format(self._quote_name(target_database_name), suffix)
with self._nodb_connection.cursor() as cursor:
try:
- cursor.execute("CREATE DATABASE %s WITH TEMPLATE %s" % (
- qn(target_database_name), qn(source_database_name)))
+ cursor.execute(creation_sql)
except Exception as e:
if keepdb:
return
@@ -35,9 +49,8 @@ class DatabaseCreation(BaseDatabaseCreation):
print("Destroying old test database for alias %s..." % (
self._get_database_display_str(verbosity, target_database_name),
))
- cursor.execute("DROP DATABASE %s" % qn(target_database_name))
- cursor.execute("CREATE DATABASE %s WITH TEMPLATE %s" % (
- qn(target_database_name), qn(source_database_name)))
+ cursor.execute("DROP DATABASE %s" % self._quote_name(target_database_name))
+ cursor.execute(creation_sql)
except Exception as e:
sys.stderr.write("Got an error cloning the test database: %s\n" % e)
sys.exit(2)
diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt
index 983b7525c3..257324330d 100644
--- a/docs/ref/databases.txt
+++ b/docs/ref/databases.txt
@@ -164,6 +164,16 @@ lookups that use the ``LIKE`` operator in their SQL, as is done with the
.. _PostgreSQL operator class: http://www.postgresql.org/docs/current/static/indexes-opclass.html
+Test database templates
+-----------------------
+
+.. versionadded:: 1.11
+
+You can use the :setting:`TEST['TEMPLATE'] <TEST_TEMPLATE>` setting to specify
+a `template`_ (e.g. ``'template0'``) from which to create a test database.
+
+.. _template: https://www.postgresql.org/docs/current/static/sql-createdatabase.html
+
Speeding up test execution with non-durable settings
----------------------------------------------------
diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt
index 445e504e98..856d9186a8 100644
--- a/docs/ref/settings.txt
+++ b/docs/ref/settings.txt
@@ -738,6 +738,20 @@ the database state between tests if you don't have transactions). You can set
this to ``False`` to speed up creation time if you don't have any test classes
with :ref:`serialized_rollback=True <test-case-serialized-rollback>`.
+.. setting:: TEST_TEMPLATE
+
+``TEMPLATE``
+^^^^^^^^^^^^
+
+.. versionadded:: 1.11
+
+This is a PostgreSQL-specific setting.
+
+The name of a `template`_ (e.g. ``'template0'``) from which to create the test
+database.
+
+.. _template: https://www.postgresql.org/docs/current/static/sql-createdatabase.html
+
.. setting:: TEST_CREATE
``CREATE_DB``
diff --git a/docs/releases/1.11.txt b/docs/releases/1.11.txt
index 802a09e6a9..b9fe8de41d 100644
--- a/docs/releases/1.11.txt
+++ b/docs/releases/1.11.txt
@@ -159,6 +159,9 @@ Database backends
on PostgreSQL 9.5+ and Oracle to execute queries with
``FOR UPDATE SKIP LOCKED``.
+* Added the :setting:`TEST['TEMPLATE'] <TEST_TEMPLATE>` setting to let
+ PostgreSQL users specify a template for creating the test database.
+
Email
~~~~~
diff --git a/tests/backends/test_creation.py b/tests/backends/test_creation.py
index 519b3f049c..d2a615a7e4 100644
--- a/tests/backends/test_creation.py
+++ b/tests/backends/test_creation.py
@@ -1,9 +1,12 @@
import copy
+import unittest
+from contextlib import contextmanager
-from django.db import DEFAULT_DB_ALIAS, connections
+from django.db import DEFAULT_DB_ALIAS, connection, connections
from django.db.backends.base.creation import (
TEST_DATABASE_PREFIX, BaseDatabaseCreation,
)
+from django.db.backends.postgresql.creation import DatabaseCreation
from django.test import SimpleTestCase
@@ -40,3 +43,48 @@ class TestDbSignatureTests(SimpleTestCase):
test_connection.settings_dict['TEST'] = {'NAME': test_name}
signature = BaseDatabaseCreation(test_connection).test_db_signature()
self.assertEqual(signature[3], test_name)
+
+
+@unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL-specific tests")
+class PostgreSQLDatabaseCreationTests(SimpleTestCase):
+
+ @contextmanager
+ def changed_test_settings(self, **kwargs):
+ settings = connection.settings_dict['TEST']
+ saved_values = {}
+ for name in kwargs:
+ if name in settings:
+ saved_values[name] = settings[name]
+
+ for name, value in kwargs.items():
+ settings[name] = value
+ try:
+ yield
+ finally:
+ for name, value in kwargs.items():
+ if name in saved_values:
+ settings[name] = saved_values[name]
+ else:
+ del settings[name]
+
+ def check_sql_table_creation_suffix(self, settings, expected):
+ with self.changed_test_settings(**settings):
+ creation = DatabaseCreation(connection)
+ suffix = creation.sql_table_creation_suffix()
+ self.assertEqual(suffix, expected)
+
+ def test_sql_table_creation_suffix_with_none_settings(self):
+ settings = dict(CHARSET=None, TEMPLATE=None)
+ self.check_sql_table_creation_suffix(settings, "")
+
+ def test_sql_table_creation_suffix_with_encoding(self):
+ settings = dict(CHARSET='UTF8')
+ self.check_sql_table_creation_suffix(settings, "WITH ENCODING 'UTF8'")
+
+ def test_sql_table_creation_suffix_with_template(self):
+ settings = dict(TEMPLATE='template0')
+ self.check_sql_table_creation_suffix(settings, 'WITH TEMPLATE "template0"')
+
+ def test_sql_table_creation_suffix_with_encoding_and_template(self):
+ settings = dict(CHARSET='UTF8', TEMPLATE='template0')
+ self.check_sql_table_creation_suffix(settings, '''WITH ENCODING 'UTF8' TEMPLATE "template0"''')