Loading django/db/backends/creation.py +22 −15 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ import warnings from django.conf import settings from django.db.utils import load_backend from django.utils.encoding import force_bytes from django.utils.functional import cached_property from django.utils.six.moves import input from .utils import truncate_name Loading @@ -29,6 +30,24 @@ class BaseDatabaseCreation(object): def __init__(self, connection): self.connection = connection @cached_property def _nodb_connection(self): """ Alternative connection to be used when there is no need to access the main database, specifically for test db creation/deletion. This also prevents the production database from being exposed to potential child threads while (or after) the test database is destroyed. Refs #10868, #17786, #16969. """ settings_dict = self.connection.settings_dict.copy() settings_dict['NAME'] = None backend = load_backend(settings_dict['ENGINE']) nodb_connection = backend.DatabaseWrapper( settings_dict, alias='__no_db__', allow_thread_sharing=False) return nodb_connection @classmethod def _digest(cls, *args): """ Loading Loading @@ -386,7 +405,7 @@ class BaseDatabaseCreation(object): qn = self.connection.ops.quote_name # Create the test database and connect to it. cursor = self.connection.cursor() cursor = self._nodb_connection.cursor() try: cursor.execute( "CREATE DATABASE %s %s" % (qn(test_database_name), suffix)) Loading Loading @@ -431,18 +450,7 @@ class BaseDatabaseCreation(object): print("Destroying test database for alias '%s'%s..." % ( self.connection.alias, test_db_repr)) # Temporarily use a new connection and a copy of the settings dict. # This prevents the production database from being exposed to potential # child threads while (or after) the test database is destroyed. # Refs #10868 and #17786. settings_dict = self.connection.settings_dict.copy() settings_dict['NAME'] = old_database_name backend = load_backend(settings_dict['ENGINE']) new_connection = backend.DatabaseWrapper( settings_dict, alias='__destroy_test_db__', allow_thread_sharing=False) new_connection.creation._destroy_test_db(test_database_name, verbosity) self._destroy_test_db(test_database_name, verbosity) def _destroy_test_db(self, test_database_name, verbosity): """ Loading @@ -452,12 +460,11 @@ class BaseDatabaseCreation(object): # ourselves. Connect to the previous database (not the test database) # to do so, because it's not allowed to delete a database while being # connected to it. cursor = self.connection.cursor() cursor = self._nodb_connection.cursor() # Wait to avoid "database is being accessed by other users" errors. time.sleep(1) cursor.execute("DROP DATABASE %s" % self.connection.ops.quote_name(test_database_name)) self.connection.close() def set_autocommit(self): """ Loading django/db/backends/postgresql_psycopg2/base.py +3 −2 Original line number Diff line number Diff line Loading @@ -101,13 +101,14 @@ class DatabaseWrapper(BaseDatabaseWrapper): def get_connection_params(self): settings_dict = self.settings_dict if not settings_dict['NAME']: # None may be used to connect to the default 'postgres' db if settings_dict['NAME'] == '': from django.core.exceptions import ImproperlyConfigured raise ImproperlyConfigured( "settings.DATABASES is improperly configured. " "Please supply the NAME value.") conn_params = { 'database': settings_dict['NAME'], 'database': settings_dict['NAME'] or 'postgres', } conn_params.update(settings_dict['OPTIONS']) if 'autocommit' in conn_params: Loading docs/internals/contributing/writing-code/unit-tests.txt +11 −7 Original line number Diff line number Diff line Loading @@ -76,17 +76,21 @@ If you're using a backend that isn't SQLite, you will need to provide other details for each database: * The :setting:`USER` option needs to specify an existing user account for the database. for the database. That user needs permission to execute ``CREATE DATABASE`` so that the test database can be created. * The :setting:`PASSWORD` option needs to provide the password for the :setting:`USER` that has been specified. * The :setting:`NAME` option must be the name of an existing database to which the given user has permission to connect. The unit tests will not touch this database; the test runner creates a new database whose name is :setting:`NAME` prefixed with ``test_``, and this test database is deleted when the tests are finished. This means your user account needs permission to execute ``CREATE DATABASE``. Test databases get their names by prepending ``test_`` to the value of the :setting:`NAME` settings for the databases defined in :setting:`DATABASES`. These test databases are deleted when the tests are finished. .. versionchanged:: 1.7 Before Django 1.7, the :setting:`NAME` setting was mandatory and had to be the name of an existing database to which the given user had permission to connect. You will also need to ensure that your database uses UTF-8 as the default character set. If your database server doesn't use UTF-8 as a default charset, Loading Loading
django/db/backends/creation.py +22 −15 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ import warnings from django.conf import settings from django.db.utils import load_backend from django.utils.encoding import force_bytes from django.utils.functional import cached_property from django.utils.six.moves import input from .utils import truncate_name Loading @@ -29,6 +30,24 @@ class BaseDatabaseCreation(object): def __init__(self, connection): self.connection = connection @cached_property def _nodb_connection(self): """ Alternative connection to be used when there is no need to access the main database, specifically for test db creation/deletion. This also prevents the production database from being exposed to potential child threads while (or after) the test database is destroyed. Refs #10868, #17786, #16969. """ settings_dict = self.connection.settings_dict.copy() settings_dict['NAME'] = None backend = load_backend(settings_dict['ENGINE']) nodb_connection = backend.DatabaseWrapper( settings_dict, alias='__no_db__', allow_thread_sharing=False) return nodb_connection @classmethod def _digest(cls, *args): """ Loading Loading @@ -386,7 +405,7 @@ class BaseDatabaseCreation(object): qn = self.connection.ops.quote_name # Create the test database and connect to it. cursor = self.connection.cursor() cursor = self._nodb_connection.cursor() try: cursor.execute( "CREATE DATABASE %s %s" % (qn(test_database_name), suffix)) Loading Loading @@ -431,18 +450,7 @@ class BaseDatabaseCreation(object): print("Destroying test database for alias '%s'%s..." % ( self.connection.alias, test_db_repr)) # Temporarily use a new connection and a copy of the settings dict. # This prevents the production database from being exposed to potential # child threads while (or after) the test database is destroyed. # Refs #10868 and #17786. settings_dict = self.connection.settings_dict.copy() settings_dict['NAME'] = old_database_name backend = load_backend(settings_dict['ENGINE']) new_connection = backend.DatabaseWrapper( settings_dict, alias='__destroy_test_db__', allow_thread_sharing=False) new_connection.creation._destroy_test_db(test_database_name, verbosity) self._destroy_test_db(test_database_name, verbosity) def _destroy_test_db(self, test_database_name, verbosity): """ Loading @@ -452,12 +460,11 @@ class BaseDatabaseCreation(object): # ourselves. Connect to the previous database (not the test database) # to do so, because it's not allowed to delete a database while being # connected to it. cursor = self.connection.cursor() cursor = self._nodb_connection.cursor() # Wait to avoid "database is being accessed by other users" errors. time.sleep(1) cursor.execute("DROP DATABASE %s" % self.connection.ops.quote_name(test_database_name)) self.connection.close() def set_autocommit(self): """ Loading
django/db/backends/postgresql_psycopg2/base.py +3 −2 Original line number Diff line number Diff line Loading @@ -101,13 +101,14 @@ class DatabaseWrapper(BaseDatabaseWrapper): def get_connection_params(self): settings_dict = self.settings_dict if not settings_dict['NAME']: # None may be used to connect to the default 'postgres' db if settings_dict['NAME'] == '': from django.core.exceptions import ImproperlyConfigured raise ImproperlyConfigured( "settings.DATABASES is improperly configured. " "Please supply the NAME value.") conn_params = { 'database': settings_dict['NAME'], 'database': settings_dict['NAME'] or 'postgres', } conn_params.update(settings_dict['OPTIONS']) if 'autocommit' in conn_params: Loading
docs/internals/contributing/writing-code/unit-tests.txt +11 −7 Original line number Diff line number Diff line Loading @@ -76,17 +76,21 @@ If you're using a backend that isn't SQLite, you will need to provide other details for each database: * The :setting:`USER` option needs to specify an existing user account for the database. for the database. That user needs permission to execute ``CREATE DATABASE`` so that the test database can be created. * The :setting:`PASSWORD` option needs to provide the password for the :setting:`USER` that has been specified. * The :setting:`NAME` option must be the name of an existing database to which the given user has permission to connect. The unit tests will not touch this database; the test runner creates a new database whose name is :setting:`NAME` prefixed with ``test_``, and this test database is deleted when the tests are finished. This means your user account needs permission to execute ``CREATE DATABASE``. Test databases get their names by prepending ``test_`` to the value of the :setting:`NAME` settings for the databases defined in :setting:`DATABASES`. These test databases are deleted when the tests are finished. .. versionchanged:: 1.7 Before Django 1.7, the :setting:`NAME` setting was mandatory and had to be the name of an existing database to which the given user had permission to connect. You will also need to ensure that your database uses UTF-8 as the default character set. If your database server doesn't use UTF-8 as a default charset, Loading