Fixed #32292 -- Added support for connection by service name to PostgreSQL.
This commit is contained in:
parent
f054468cac
commit
dcb3ad3319
|
@ -152,10 +152,14 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
def get_connection_params(self):
|
def get_connection_params(self):
|
||||||
settings_dict = self.settings_dict
|
settings_dict = self.settings_dict
|
||||||
# None may be used to connect to the default 'postgres' db
|
# None may be used to connect to the default 'postgres' db
|
||||||
if settings_dict['NAME'] == '':
|
if (
|
||||||
|
settings_dict['NAME'] == '' and
|
||||||
|
not settings_dict.get('OPTIONS', {}).get('service')
|
||||||
|
):
|
||||||
raise ImproperlyConfigured(
|
raise ImproperlyConfigured(
|
||||||
"settings.DATABASES is improperly configured. "
|
"settings.DATABASES is improperly configured. "
|
||||||
"Please supply the NAME value.")
|
"Please supply the NAME or OPTIONS['service'] value."
|
||||||
|
)
|
||||||
if len(settings_dict['NAME'] or '') > self.ops.max_name_length():
|
if len(settings_dict['NAME'] or '') > self.ops.max_name_length():
|
||||||
raise ImproperlyConfigured(
|
raise ImproperlyConfigured(
|
||||||
"The database name '%s' (%d characters) is longer than "
|
"The database name '%s' (%d characters) is longer than "
|
||||||
|
@ -166,10 +170,19 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
self.ops.max_name_length(),
|
self.ops.max_name_length(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
conn_params = {}
|
||||||
|
if settings_dict['NAME']:
|
||||||
conn_params = {
|
conn_params = {
|
||||||
'database': settings_dict['NAME'] or 'postgres',
|
'database': settings_dict['NAME'],
|
||||||
**settings_dict['OPTIONS'],
|
**settings_dict['OPTIONS'],
|
||||||
}
|
}
|
||||||
|
elif settings_dict['NAME'] is None:
|
||||||
|
# Connect to the default 'postgres' db.
|
||||||
|
settings_dict.get('OPTIONS', {}).pop('service', None)
|
||||||
|
conn_params = {'database': 'postgres', **settings_dict['OPTIONS']}
|
||||||
|
else:
|
||||||
|
conn_params = {**settings_dict['OPTIONS']}
|
||||||
|
|
||||||
conn_params.pop('isolation_level', None)
|
conn_params.pop('isolation_level', None)
|
||||||
if settings_dict['USER']:
|
if settings_dict['USER']:
|
||||||
conn_params['user'] = settings_dict['USER']
|
conn_params['user'] = settings_dict['USER']
|
||||||
|
|
|
@ -16,6 +16,7 @@ class DatabaseClient(BaseDatabaseClient):
|
||||||
dbname = settings_dict.get('NAME') or 'postgres'
|
dbname = settings_dict.get('NAME') or 'postgres'
|
||||||
user = settings_dict.get('USER')
|
user = settings_dict.get('USER')
|
||||||
passwd = settings_dict.get('PASSWORD')
|
passwd = settings_dict.get('PASSWORD')
|
||||||
|
service = options.get('service')
|
||||||
sslmode = options.get('sslmode')
|
sslmode = options.get('sslmode')
|
||||||
sslrootcert = options.get('sslrootcert')
|
sslrootcert = options.get('sslrootcert')
|
||||||
sslcert = options.get('sslcert')
|
sslcert = options.get('sslcert')
|
||||||
|
@ -33,6 +34,8 @@ class DatabaseClient(BaseDatabaseClient):
|
||||||
env = {}
|
env = {}
|
||||||
if passwd:
|
if passwd:
|
||||||
env['PGPASSWORD'] = str(passwd)
|
env['PGPASSWORD'] = str(passwd)
|
||||||
|
if service:
|
||||||
|
env['PGSERVICE'] = str(service)
|
||||||
if sslmode:
|
if sslmode:
|
||||||
env['PGSSLMODE'] = str(sslmode)
|
env['PGSSLMODE'] = str(sslmode)
|
||||||
if sslrootcert:
|
if sslrootcert:
|
||||||
|
|
|
@ -108,11 +108,43 @@ required, though the latest release is recommended.
|
||||||
|
|
||||||
.. _psycopg2: https://www.psycopg.org/
|
.. _psycopg2: https://www.psycopg.org/
|
||||||
|
|
||||||
|
.. _postgresql-connection-settings:
|
||||||
|
|
||||||
PostgreSQL connection settings
|
PostgreSQL connection settings
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
See :setting:`HOST` for details.
|
See :setting:`HOST` for details.
|
||||||
|
|
||||||
|
To connect using a service name from the `connection service file`_, you must
|
||||||
|
specify it in the :setting:`OPTIONS` part of your database configuration in
|
||||||
|
:setting:`DATABASES`:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: settings.py
|
||||||
|
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.postgresql',
|
||||||
|
'OPTIONS': {'service': 'my_service'},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
:caption: .pg_service.conf
|
||||||
|
|
||||||
|
[my_service]
|
||||||
|
host=localhost
|
||||||
|
user=USER
|
||||||
|
dbname=NAME
|
||||||
|
password=PASSWORD
|
||||||
|
port=5432
|
||||||
|
|
||||||
|
.. _connection service file: https://www.postgresql.org/docs/current/libpq-pgservice.html
|
||||||
|
|
||||||
|
.. versionchanged:: 4.0
|
||||||
|
|
||||||
|
Support for connecting by a service name was added.
|
||||||
|
|
||||||
Optimizing PostgreSQL's configuration
|
Optimizing PostgreSQL's configuration
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,8 @@ Minor features
|
||||||
:mod:`django.contrib.postgres`
|
:mod:`django.contrib.postgres`
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
* ...
|
* The PostgreSQL backend now supports connecting by a service name. See
|
||||||
|
:ref:`postgresql-connection-settings` for more details.
|
||||||
|
|
||||||
:mod:`django.contrib.redirects`
|
:mod:`django.contrib.redirects`
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
|
@ -68,6 +68,36 @@ class Tests(TestCase):
|
||||||
with self.assertRaisesMessage(ImproperlyConfigured, msg):
|
with self.assertRaisesMessage(ImproperlyConfigured, msg):
|
||||||
DatabaseWrapper(settings).get_connection_params()
|
DatabaseWrapper(settings).get_connection_params()
|
||||||
|
|
||||||
|
def test_database_name_empty(self):
|
||||||
|
from django.db.backends.postgresql.base import DatabaseWrapper
|
||||||
|
settings = connection.settings_dict.copy()
|
||||||
|
settings['NAME'] = ''
|
||||||
|
msg = (
|
||||||
|
"settings.DATABASES is improperly configured. Please supply the "
|
||||||
|
"NAME or OPTIONS['service'] value."
|
||||||
|
)
|
||||||
|
with self.assertRaisesMessage(ImproperlyConfigured, msg):
|
||||||
|
DatabaseWrapper(settings).get_connection_params()
|
||||||
|
|
||||||
|
def test_service_name(self):
|
||||||
|
from django.db.backends.postgresql.base import DatabaseWrapper
|
||||||
|
settings = connection.settings_dict.copy()
|
||||||
|
settings['OPTIONS'] = {'service': 'my_service'}
|
||||||
|
settings['NAME'] = ''
|
||||||
|
params = DatabaseWrapper(settings).get_connection_params()
|
||||||
|
self.assertEqual(params['service'], 'my_service')
|
||||||
|
self.assertNotIn('database', params)
|
||||||
|
|
||||||
|
def test_service_name_default_db(self):
|
||||||
|
# None is used to connect to the default 'postgres' db.
|
||||||
|
from django.db.backends.postgresql.base import DatabaseWrapper
|
||||||
|
settings = connection.settings_dict.copy()
|
||||||
|
settings['NAME'] = None
|
||||||
|
settings['OPTIONS'] = {'service': 'django_test'}
|
||||||
|
params = DatabaseWrapper(settings).get_connection_params()
|
||||||
|
self.assertEqual(params['database'], 'postgres')
|
||||||
|
self.assertNotIn('service', params)
|
||||||
|
|
||||||
def test_connect_and_rollback(self):
|
def test_connect_and_rollback(self):
|
||||||
"""
|
"""
|
||||||
PostgreSQL shouldn't roll back SET TIME ZONE, even if the first
|
PostgreSQL shouldn't roll back SET TIME ZONE, even if the first
|
||||||
|
|
|
@ -67,6 +67,12 @@ class PostgreSqlDbshellCommandTestCase(SimpleTestCase):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_service(self):
|
||||||
|
self.assertEqual(
|
||||||
|
self.settings_to_cmd_args_env({'OPTIONS': {'service': 'django_test'}}),
|
||||||
|
(['psql', 'postgres'], {'PGSERVICE': 'django_test'}),
|
||||||
|
)
|
||||||
|
|
||||||
def test_column(self):
|
def test_column(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.settings_to_cmd_args_env({
|
self.settings_to_cmd_args_env({
|
||||||
|
|
Loading…
Reference in New Issue