From e8ea0a64fa58cdea6dd0ca3b739186a649ebf5b1 Mon Sep 17 00:00:00 2001 From: Jeremy Shute Date: Mon, 23 Mar 2015 16:03:20 -0400 Subject: [PATCH 1/5] supports database options --- dj_database_url.py | 25 +++++++++++++++++++++++++ test_dj_database_url.py | 21 +++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/dj_database_url.py b/dj_database_url.py index 8a73c71..4212603 100644 --- a/dj_database_url.py +++ b/dj_database_url.py @@ -89,9 +89,34 @@ def parse(url, engine=None): 'PORT': url.port or '', }) + # Parse the query string into OPTIONS. + qs = urlparse.parse_qs(url.query) + options = {} + for k in qs: + options[k] = qs[k][-1] + if options: + config['OPTIONS'] = options + if engine: config['ENGINE'] = engine elif url.scheme in SCHEMES: config['ENGINE'] = SCHEMES[url.scheme] return config + + +def main(): + import django.db + from django.conf import settings + default = django.db.DEFAULT_DB_ALIAS + settings.configure() + settings.DATABASES[default] = config() + db = django.db.connections[default] + db.connect() + import pprint + pprint.pprint(db.settings_dict) + print db.is_usable() + + +if __name__ == "__main__": + main() diff --git a/test_dj_database_url.py b/test_dj_database_url.py index 001960a..f8d3f0a 100644 --- a/test_dj_database_url.py +++ b/test_dj_database_url.py @@ -111,5 +111,26 @@ class DatabaseTestSuite(unittest.TestCase): assert url['ENGINE'] == engine + def test_database_url_with_options(self): + del os.environ['DATABASE_URL'] + a = dj_database_url.config() + assert not a + + os.environ['DATABASE_URL'] = 'postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?sslrootcert=rds-combined-ca-bundle.pem&sslmode=verify-full' + + url = dj_database_url.config() + + assert url['ENGINE'] == 'django.db.backends.postgresql_psycopg2' + assert url['NAME'] == 'd8r82722r2kuvn' + assert url['HOST'] == 'ec2-107-21-253-135.compute-1.amazonaws.com' + assert url['USER'] == 'uf07k1i6d8ia0v' + assert url['PASSWORD'] == 'wegauwhgeuioweg' + assert url['PORT'] == 5431 + assert url['OPTIONS'] == { + 'sslrootcert': 'rds-combined-ca-bundle.pem', + 'sslmode': 'verify-full' + } + + if __name__ == '__main__': unittest.main() From 5336a0c0141b1ee9186c445590f2ece46b867123 Mon Sep 17 00:00:00 2001 From: Sam Sandberg Date: Thu, 14 May 2015 17:14:54 -0400 Subject: [PATCH 2/5] Don't need this `main()` stuff for prod --- dj_database_url.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/dj_database_url.py b/dj_database_url.py index 4212603..9c93b5a 100644 --- a/dj_database_url.py +++ b/dj_database_url.py @@ -103,20 +103,3 @@ def parse(url, engine=None): config['ENGINE'] = SCHEMES[url.scheme] return config - - -def main(): - import django.db - from django.conf import settings - default = django.db.DEFAULT_DB_ALIAS - settings.configure() - settings.DATABASES[default] = config() - db = django.db.connections[default] - db.connect() - import pprint - pprint.pprint(db.settings_dict) - print db.is_usable() - - -if __name__ == "__main__": - main() From 7f6717e4340805d638b7ff00c8ae3bc57159a262 Mon Sep 17 00:00:00 2001 From: Sam Sandberg Date: Thu, 14 May 2015 17:52:16 -0400 Subject: [PATCH 3/5] Cleaned up querystring parsing Moved `_parse_querystring_for_options()` into its own function Added test for empty querystring params --- dj_database_url.py | 21 ++++++++++++++------- test_dj_database_url.py | 11 ++++++----- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/dj_database_url.py b/dj_database_url.py index 9c93b5a..2aa7b3c 100644 --- a/dj_database_url.py +++ b/dj_database_url.py @@ -48,6 +48,17 @@ def config(env=DEFAULT_ENV, default=None, engine=None): return config +def _parse_querystring_for_options(path): + if '?' not in path: + return + query_string = path.split('?', 2)[1] + if not query_string: + return + qs = urlparse.parse_qs(query_string) + return {key: values[-1] for key, values in qs.iteritems()} + + + def parse(url, engine=None): """Parses a database URL.""" @@ -66,9 +77,8 @@ def parse(url, engine=None): url = urlparse.urlparse(url) - # Remove query strings. - path = url.path[1:] - path = path.split('?', 2)[0] + # Path (without leading '/'), and with no query string + path = url.path[1:].split('?')[0] # If we are using sqlite and we have no path, then assume we # want an in-memory database (this is the behaviour of sqlalchemy) @@ -90,10 +100,7 @@ def parse(url, engine=None): }) # Parse the query string into OPTIONS. - qs = urlparse.parse_qs(url.query) - options = {} - for k in qs: - options[k] = qs[k][-1] + options = _parse_querystring_for_options(url.path) if options: config['OPTIONS'] = options diff --git a/test_dj_database_url.py b/test_dj_database_url.py index f8d3f0a..b457008 100644 --- a/test_dj_database_url.py +++ b/test_dj_database_url.py @@ -112,12 +112,8 @@ class DatabaseTestSuite(unittest.TestCase): assert url['ENGINE'] == engine def test_database_url_with_options(self): - del os.environ['DATABASE_URL'] - a = dj_database_url.config() - assert not a - + # Test full options os.environ['DATABASE_URL'] = 'postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?sslrootcert=rds-combined-ca-bundle.pem&sslmode=verify-full' - url = dj_database_url.config() assert url['ENGINE'] == 'django.db.backends.postgresql_psycopg2' @@ -131,6 +127,11 @@ class DatabaseTestSuite(unittest.TestCase): 'sslmode': 'verify-full' } + # Test empty options + os.environ['DATABASE_URL'] = 'postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?' + url = dj_database_url.config() + assert 'OPTIONS' not in url + if __name__ == '__main__': unittest.main() From 4cc995adc72189b269de9bb196e933238c9a1ad8 Mon Sep 17 00:00:00 2001 From: Sam Sandberg Date: Thu, 14 May 2015 18:05:12 -0400 Subject: [PATCH 4/5] Travis didn't like my dict comprehension D: --- dj_database_url.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dj_database_url.py b/dj_database_url.py index 2aa7b3c..5005524 100644 --- a/dj_database_url.py +++ b/dj_database_url.py @@ -55,8 +55,10 @@ def _parse_querystring_for_options(path): if not query_string: return qs = urlparse.parse_qs(query_string) - return {key: values[-1] for key, values in qs.iteritems()} - + result = {} + for key, values in qs.iteritems(): + result[key] = values[-1] + return result def parse(url, engine=None): From b83dfb52ce3126e8e76ee2553aa22c90bb45fbd0 Mon Sep 17 00:00:00 2001 From: Sam Sandberg Date: Fri, 15 May 2015 12:32:15 -0400 Subject: [PATCH 5/5] Simplified the querystring parse logic Apparently I was targetting an older version of python 2.7 where urlparse worked differently --- dj_database_url.py | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/dj_database_url.py b/dj_database_url.py index 5005524..0ed232b 100644 --- a/dj_database_url.py +++ b/dj_database_url.py @@ -48,19 +48,6 @@ def config(env=DEFAULT_ENV, default=None, engine=None): return config -def _parse_querystring_for_options(path): - if '?' not in path: - return - query_string = path.split('?', 2)[1] - if not query_string: - return - qs = urlparse.parse_qs(query_string) - result = {} - for key, values in qs.iteritems(): - result[key] = values[-1] - return result - - def parse(url, engine=None): """Parses a database URL.""" @@ -102,7 +89,10 @@ def parse(url, engine=None): }) # Parse the query string into OPTIONS. - options = _parse_querystring_for_options(url.path) + qs = urlparse.parse_qs(url.query) + options = {} + for key, values in qs.iteritems(): + options[key] = values[-1] if options: config['OPTIONS'] = options