dj-database-url/dj_database_url.py

158 lines
4.8 KiB
Python
Raw Normal View History

2012-04-30 17:37:05 +00:00
# -*- coding: utf-8 -*-
import os
2012-06-19 15:53:51 +00:00
try:
import urlparse
except ImportError:
import urllib.parse as urlparse
try:
from django import VERSION as DJANGO_VERSION
except ImportError:
DJANGO_VERSION = None
2012-06-19 15:53:51 +00:00
2012-04-30 17:37:05 +00:00
# Register database schemes in URLs.
urlparse.uses_netloc.append('postgres')
2012-06-19 15:21:06 +00:00
urlparse.uses_netloc.append('postgresql')
urlparse.uses_netloc.append('pgsql')
2012-06-05 02:37:58 +00:00
urlparse.uses_netloc.append('postgis')
2012-04-30 17:37:05 +00:00
urlparse.uses_netloc.append('mysql')
2012-06-19 15:21:06 +00:00
urlparse.uses_netloc.append('mysql2')
urlparse.uses_netloc.append('mysqlgis')
urlparse.uses_netloc.append('mysql-connector')
2017-08-21 09:59:09 +00:00
urlparse.uses_netloc.append('mssql')
2013-02-04 15:24:58 +00:00
urlparse.uses_netloc.append('spatialite')
2012-05-01 16:23:09 +00:00
urlparse.uses_netloc.append('sqlite')
2014-10-24 18:48:14 +00:00
urlparse.uses_netloc.append('oracle')
urlparse.uses_netloc.append('oraclegis')
2016-10-25 08:25:01 +00:00
urlparse.uses_netloc.append('redshift')
2012-04-30 17:37:05 +00:00
2012-04-30 18:33:12 +00:00
DEFAULT_ENV = 'DATABASE_URL'
2012-04-30 17:37:05 +00:00
2012-06-19 14:57:33 +00:00
SCHEMES = {
'postgis': 'django.contrib.gis.db.backends.postgis',
'mysql': 'django.db.backends.mysql',
'mysql2': 'django.db.backends.mysql',
'mysqlgis': 'django.contrib.gis.db.backends.mysql',
'mysql-connector': 'mysql.connector.django',
2017-08-21 09:59:09 +00:00
'mssql': 'sql_server.pyodbc',
2013-02-01 16:33:44 +00:00
'spatialite': 'django.contrib.gis.db.backends.spatialite',
'sqlite': 'django.db.backends.sqlite3',
2014-10-24 18:48:14 +00:00
'oracle': 'django.db.backends.oracle',
'oraclegis': 'django.contrib.gis.db.backends.oracle',
2016-10-25 08:25:01 +00:00
'redshift': 'django_redshift_backend',
2012-06-19 14:57:33 +00:00
}
# https://docs.djangoproject.com/en/2.0/releases/2.0/#id1
if DJANGO_VERSION and DJANGO_VERSION < (2, 0):
SCHEMES['postgres'] = 'django.db.backends.postgresql_psycopg2'
SCHEMES['postgresql'] = 'django.db.backends.postgresql_psycopg2'
SCHEMES['pgsql'] = 'django.db.backends.postgresql_psycopg2'
else:
SCHEMES['postgres'] = 'django.db.backends.postgresql'
SCHEMES['postgresql'] = 'django.db.backends.postgresql'
SCHEMES['pgsql'] = 'django.db.backends.postgresql'
2012-06-19 14:57:33 +00:00
def config(env=DEFAULT_ENV, default=None, engine=None, conn_max_age=0, ssl_require=False):
2012-04-30 18:55:37 +00:00
"""Returns configured DATABASE dictionary from DATABASE_URL."""
2012-04-30 17:37:05 +00:00
2012-04-30 18:33:12 +00:00
config = {}
2012-04-30 17:37:05 +00:00
s = os.environ.get(env, default)
2012-05-30 05:23:28 +00:00
if s:
config = parse(s, engine, conn_max_age, ssl_require)
2012-04-30 18:55:37 +00:00
return config
def parse(url, engine=None, conn_max_age=0, ssl_require=False):
2012-04-30 18:55:37 +00:00
"""Parses a database URL."""
if url == 'sqlite://:memory:':
# this is a special case, because if we pass this URL into
# urlparse, urlparse will choke trying to interpret "memory"
# as a port number
return {
'ENGINE': SCHEMES['sqlite'],
'NAME': ':memory:'
}
2013-01-05 10:28:27 +00:00
# note: no other settings are required for sqlite
# otherwise parse the url as normal
2012-04-30 18:55:37 +00:00
config = {}
url = urlparse.urlparse(url)
2016-02-03 00:07:35 +00:00
# Split query strings from path.
path = url.path[1:]
if '?' in path and not url.query:
path, query = path.split('?', 2)
else:
path, query = path, url.query
query = urlparse.parse_qs(query)
2012-06-19 15:21:06 +00:00
# If we are using sqlite and we have no path, then assume we
# want an in-memory database (this is the behaviour of sqlalchemy)
if url.scheme == 'sqlite' and path == '':
path = ':memory:'
# Handle postgres percent-encoded paths.
2017-01-20 15:06:53 +00:00
hostname = url.hostname or ''
if '%2f' in hostname.lower():
2017-01-20 15:06:53 +00:00
# Switch to url.netloc to avoid lower cased paths
hostname = url.netloc
if "@" in hostname:
hostname = hostname.rsplit("@", 1)[1]
if ":" in hostname:
hostname = hostname.split(":", 1)[0]
hostname = hostname.replace('%2f', '/').replace('%2F', '/')
2017-04-08 10:34:30 +00:00
# Lookup specified engine.
engine = SCHEMES[url.scheme] if engine is None else engine
2018-02-07 20:33:10 +00:00
port = (str(url.port) if url.port and engine in [SCHEMES['oracle'], SCHEMES['mssql']]
2017-04-08 10:34:30 +00:00
else url.port)
2012-04-30 18:55:37 +00:00
# Update with environment configuration.
config.update({
'NAME': urlparse.unquote(path or ''),
'USER': urlparse.unquote(url.username or ''),
'PASSWORD': urlparse.unquote(url.password or ''),
'HOST': hostname,
2017-04-08 10:34:30 +00:00
'PORT': port or '',
'CONN_MAX_AGE': conn_max_age,
2012-04-30 18:55:37 +00:00
})
2016-02-03 00:07:35 +00:00
# Pass the query string into OPTIONS.
options = {}
2016-02-03 00:07:35 +00:00
for key, values in query.items():
if url.scheme == 'mysql' and key == 'ssl-ca':
options['ssl'] = {'ca': values[-1]}
continue
options[key] = values[-1]
2016-02-03 00:07:35 +00:00
if ssl_require:
options['sslmode'] = 'require'
2016-02-03 00:07:35 +00:00
# Support for Postgres Schema URLs
if 'currentSchema' in options and engine in (
'django.contrib.gis.db.backends.postgis',
'django.db.backends.postgresql_psycopg2',
'django.db.backends.postgresql',
'django_redshift_backend',
):
options['options'] = '-c search_path={0}'.format(options.pop('currentSchema'))
2016-02-03 00:07:35 +00:00
2015-03-23 20:03:20 +00:00
if options:
config['OPTIONS'] = options
2013-12-15 16:59:18 +00:00
if engine:
config['ENGINE'] = engine
2012-05-01 16:23:09 +00:00
return config