Skip to content

Commit

Permalink
Fixed #32292 -- Added support for connection by service name to Postg…
Browse files Browse the repository at this point in the history
…reSQL.
  • Loading branch information
hramezani authored and felixxm committed Jan 20, 2021
1 parent f054468 commit dcb3ad3
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 7 deletions.
25 changes: 19 additions & 6 deletions django/db/backends/postgresql/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,14 @@ class DatabaseWrapper(BaseDatabaseWrapper):
def get_connection_params(self):
settings_dict = self.settings_dict
# 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(
"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():
raise ImproperlyConfigured(
"The database name '%s' (%d characters) is longer than "
Expand All @@ -166,10 +170,19 @@ def get_connection_params(self):
self.ops.max_name_length(),
)
)
conn_params = {
'database': settings_dict['NAME'] or 'postgres',
**settings_dict['OPTIONS'],
}
conn_params = {}
if settings_dict['NAME']:
conn_params = {
'database': settings_dict['NAME'],
**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)
if settings_dict['USER']:
conn_params['user'] = settings_dict['USER']
Expand Down
3 changes: 3 additions & 0 deletions django/db/backends/postgresql/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def settings_to_cmd_args_env(cls, settings_dict, parameters):
dbname = settings_dict.get('NAME') or 'postgres'
user = settings_dict.get('USER')
passwd = settings_dict.get('PASSWORD')
service = options.get('service')
sslmode = options.get('sslmode')
sslrootcert = options.get('sslrootcert')
sslcert = options.get('sslcert')
Expand All @@ -33,6 +34,8 @@ def settings_to_cmd_args_env(cls, settings_dict, parameters):
env = {}
if passwd:
env['PGPASSWORD'] = str(passwd)
if service:
env['PGSERVICE'] = str(service)
if sslmode:
env['PGSSLMODE'] = str(sslmode)
if sslrootcert:
Expand Down
32 changes: 32 additions & 0 deletions docs/ref/databases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,43 @@ required, though the latest release is recommended.

.. _psycopg2: https://www.psycopg.org/

.. _postgresql-connection-settings:

PostgreSQL connection settings
-------------------------------

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
-------------------------------------

Expand Down
3 changes: 2 additions & 1 deletion docs/releases/4.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ Minor features
: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`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
30 changes: 30 additions & 0 deletions tests/backends/postgresql/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,36 @@ def test_database_name_too_long(self):
with self.assertRaisesMessage(ImproperlyConfigured, msg):
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):
"""
PostgreSQL shouldn't roll back SET TIME ZONE, even if the first
Expand Down
6 changes: 6 additions & 0 deletions tests/dbshell/test_postgresql.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ def test_ssl_certificate(self):
)
)

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):
self.assertEqual(
self.settings_to_cmd_args_env({
Expand Down

0 comments on commit dcb3ad3

Please sign in to comment.