summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaude Paroz <claude@2xlibre.net>2015-05-14 19:27:31 +0200
committerClaude Paroz <claude@2xlibre.net>2015-05-15 18:44:20 +0200
commit3226050358cfcffcb14f7e48e3a5cee873388406 (patch)
tree470e446939b422a6764df8ef393396e3663f1662
parent2dee853ed4def42b7ef1b3b472b395055543cc00 (diff)
Fixed #24791 -- Added fallback when 'postgres' database isn't available
Thanks Carl Meyer and Tim Graham for the reviews.
-rw-r--r--django/db/backends/base/creation.py2
-rw-r--r--django/db/backends/postgresql_psycopg2/base.py26
-rw-r--r--docs/releases/1.8.2.txt4
-rw-r--r--tests/backends/tests.py26
4 files changed, 57 insertions, 1 deletions
diff --git a/django/db/backends/base/creation.py b/django/db/backends/base/creation.py
index 9bb9657b47..c7bf97ba46 100644
--- a/django/db/backends/base/creation.py
+++ b/django/db/backends/base/creation.py
@@ -217,7 +217,7 @@ 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.
- with self._nodb_connection.cursor() as cursor:
+ with self.connection._nodb_connection.cursor() as cursor:
# Wait to avoid "database is being accessed by other users" errors.
time.sleep(1)
cursor.execute("DROP DATABASE %s"
diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py
index d110b120d7..a76ca3c0df 100644
--- a/django/db/backends/postgresql_psycopg2/base.py
+++ b/django/db/backends/postgresql_psycopg2/base.py
@@ -4,10 +4,14 @@ PostgreSQL database backend for Django.
Requires psycopg 2: http://initd.org/projects/psycopg2
"""
+import warnings
+
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
+from django.db import DEFAULT_DB_ALIAS
from django.db.backends.base.base import BaseDatabaseWrapper
from django.db.backends.base.validation import BaseDatabaseValidation
+from django.db.utils import DatabaseError as WrappedDatabaseError
from django.utils.encoding import force_str
from django.utils.functional import cached_property
from django.utils.safestring import SafeBytes, SafeText
@@ -231,6 +235,28 @@ class DatabaseWrapper(BaseDatabaseWrapper):
return True
@cached_property
+ def _nodb_connection(self):
+ nodb_connection = super(DatabaseWrapper, self)._nodb_connection
+ try:
+ nodb_connection.ensure_connection()
+ except (DatabaseError, WrappedDatabaseError):
+ warnings.warn(
+ "Normally Django will use a connection to the 'postgres' database "
+ "to avoid running initialization queries against the production "
+ "database when it's not needed (for example, when running tests). "
+ "Django was unable to create a connection to the 'postgres' database "
+ "and will use the default database instead.",
+ RuntimeWarning
+ )
+ settings_dict = self.settings_dict.copy()
+ settings_dict['NAME'] = settings.DATABASES[DEFAULT_DB_ALIAS]['NAME']
+ nodb_connection = self.__class__(
+ self.settings_dict.copy(),
+ alias=self.alias,
+ allow_thread_sharing=False)
+ return nodb_connection
+
+ @cached_property
def psycopg2_version(self):
return PSYCOPG2_VERSION
diff --git a/docs/releases/1.8.2.txt b/docs/releases/1.8.2.txt
index 3682126aa1..896f4d0334 100644
--- a/docs/releases/1.8.2.txt
+++ b/docs/releases/1.8.2.txt
@@ -33,3 +33,7 @@ Bugfixes
* Fixed session cookie deletion when using :setting:`SESSION_COOKIE_DOMAIN`
(:ticket:`24799`).
+
+* On PostgreSQL, when no access is granted for the ``postgres`` database,
+ Django now falls back to the default database when it normally requires a
+ "no database" connection (:ticket:`24791`).
diff --git a/tests/backends/tests.py b/tests/backends/tests.py
index 737efae411..b735071ff9 100644
--- a/tests/backends/tests.py
+++ b/tests/backends/tests.py
@@ -161,6 +161,32 @@ class PostgreSQLTests(TestCase):
self.assert_parses("PostgreSQL 9.4beta1", 90400)
self.assert_parses("PostgreSQL 9.3.1 on i386-apple-darwin9.2.2, compiled by GCC i686-apple-darwin9-gcc-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5478)", 90301)
+ def test_nodb_connection(self):
+ """
+ Test that the _nodb_connection property fallbacks to the default connection
+ database when access to the 'postgres' database is not granted.
+ """
+ def mocked_connect(self):
+ if self.settings_dict['NAME'] is None:
+ raise DatabaseError()
+ return ''
+
+ nodb_conn = connection._nodb_connection
+ self.assertIsNone(nodb_conn.settings_dict['NAME'])
+
+ # Now assume the 'postgres' db isn't available
+ del connection._nodb_connection
+ with warnings.catch_warnings(record=True) as w:
+ with mock.patch('django.db.backends.base.base.BaseDatabaseWrapper.connect',
+ side_effect=mocked_connect, autospec=True):
+ nodb_conn = connection._nodb_connection
+ del connection._nodb_connection
+ self.assertIsNotNone(nodb_conn.settings_dict['NAME'])
+ self.assertEqual(nodb_conn.settings_dict['NAME'], settings.DATABASES[DEFAULT_DB_ALIAS]['NAME'])
+ # Check a RuntimeWarning nas been emitted
+ self.assertEqual(len(w), 1)
+ self.assertEqual(w[0].message.__class__, RuntimeWarning)
+
def test_version_detection(self):
"""Test PostgreSQL version detection"""