Fixed #24791 -- Added fallback when 'postgres' database isn't available
Thanks Carl Meyer and Tim Graham for the reviews.
This commit is contained in:
parent
2dee853ed4
commit
3226050358
|
@ -217,7 +217,7 @@ class BaseDatabaseCreation(object):
|
||||||
# ourselves. Connect to the previous database (not the test database)
|
# ourselves. Connect to the previous database (not the test database)
|
||||||
# to do so, because it's not allowed to delete a database while being
|
# to do so, because it's not allowed to delete a database while being
|
||||||
# connected to it.
|
# 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.
|
# Wait to avoid "database is being accessed by other users" errors.
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
cursor.execute("DROP DATABASE %s"
|
cursor.execute("DROP DATABASE %s"
|
||||||
|
|
|
@ -4,10 +4,14 @@ PostgreSQL database backend for Django.
|
||||||
Requires psycopg 2: http://initd.org/projects/psycopg2
|
Requires psycopg 2: http://initd.org/projects/psycopg2
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import warnings
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
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.base import BaseDatabaseWrapper
|
||||||
from django.db.backends.base.validation import BaseDatabaseValidation
|
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.encoding import force_str
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.safestring import SafeBytes, SafeText
|
from django.utils.safestring import SafeBytes, SafeText
|
||||||
|
@ -230,6 +234,28 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
else:
|
else:
|
||||||
return True
|
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
|
@cached_property
|
||||||
def psycopg2_version(self):
|
def psycopg2_version(self):
|
||||||
return PSYCOPG2_VERSION
|
return PSYCOPG2_VERSION
|
||||||
|
|
|
@ -33,3 +33,7 @@ Bugfixes
|
||||||
|
|
||||||
* Fixed session cookie deletion when using :setting:`SESSION_COOKIE_DOMAIN`
|
* Fixed session cookie deletion when using :setting:`SESSION_COOKIE_DOMAIN`
|
||||||
(:ticket:`24799`).
|
(: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`).
|
||||||
|
|
|
@ -161,6 +161,32 @@ class PostgreSQLTests(TestCase):
|
||||||
self.assert_parses("PostgreSQL 9.4beta1", 90400)
|
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)
|
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):
|
def test_version_detection(self):
|
||||||
"""Test PostgreSQL version detection"""
|
"""Test PostgreSQL version detection"""
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue