Skip to content

Commit

Permalink
Added support for Timescale and Timescale (GIS)
Browse files Browse the repository at this point in the history
Timescale is a Postgres based database for time series data and it's
supported via the django-timescaledb package.

[pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
  • Loading branch information
lino committed Jul 10, 2022
1 parent 66bf061 commit d60104d
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 28 deletions.
62 changes: 34 additions & 28 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Supported Databases
-------------------

Support currently exists for PostgreSQL, PostGIS, MySQL, MySQL (GIS),
Oracle, Oracle (GIS), Redshift, CockroachDB, and SQLite.
Oracle, Oracle (GIS), Redshift, CockroachDB, Timescale, Timescale (GIS) and SQLite.

Installation
------------
Expand Down Expand Up @@ -85,33 +85,37 @@ and should instead be passed as::
URL schema
----------

+-------------+-----------------------------------------------+--------------------------------------------------+
| Engine | Django Backend | URL |
+=============+===============================================+==================================================+
| PostgreSQL | ``django.db.backends.postgresql`` [1]_ | ``postgres://USER:PASSWORD@HOST:PORT/NAME`` [2]_ |
+-------------+-----------------------------------------------+--------------------------------------------------+
| PostGIS | ``django.contrib.gis.db.backends.postgis`` | ``postgis://USER:PASSWORD@HOST:PORT/NAME`` |
+-------------+-----------------------------------------------+--------------------------------------------------+
| MSSQL | ``sql_server.pyodbc`` | ``mssql://USER:PASSWORD@HOST:PORT/NAME`` |
+-------------+-----------------------------------------------+--------------------------------------------------+
| MSSQL [5]_ | ``mssql`` | ``mssqlms://USER:PASSWORD@HOST:PORT/NAME`` |
+-------------+-----------------------------------------------+--------------------------------------------------+
| MySQL | ``django.db.backends.mysql`` | ``mysql://USER:PASSWORD@HOST:PORT/NAME`` |
+-------------+-----------------------------------------------+--------------------------------------------------+
| MySQL (GIS) | ``django.contrib.gis.db.backends.mysql`` | ``mysqlgis://USER:PASSWORD@HOST:PORT/NAME`` |
+-------------+-----------------------------------------------+--------------------------------------------------+
| SQLite | ``django.db.backends.sqlite3`` | ``sqlite:///PATH`` [3]_ |
+-------------+-----------------------------------------------+--------------------------------------------------+
| SpatiaLite | ``django.contrib.gis.db.backends.spatialite`` | ``spatialite:///PATH`` [3]_ |
+-------------+-----------------------------------------------+--------------------------------------------------+
| Oracle | ``django.db.backends.oracle`` | ``oracle://USER:PASSWORD@HOST:PORT/NAME`` [4]_ |
+-------------+-----------------------------------------------+--------------------------------------------------+
| Oracle (GIS)| ``django.contrib.gis.db.backends.oracle`` | ``oraclegis://USER:PASSWORD@HOST:PORT/NAME`` |
+-------------+-----------------------------------------------+--------------------------------------------------+
| Redshift | ``django_redshift_backend`` | ``redshift://USER:PASSWORD@HOST:PORT/NAME`` |
+-------------+-----------------------------------------------+--------------------------------------------------+
| CockroachDB | ``django_cockroachdb`` | ``cockroach://USER:PASSWORD@HOST:PORT/NAME`` |
+-------------+-----------------------------------------------+--------------------------------------------------+
+----------------------+-----------------------------------------------+--------------------------------------------------+
| Engine | Django Backend | URL |
+======================+===============================================+==================================================+
| PostgreSQL | ``django.db.backends.postgresql`` [1]_ | ``postgres://USER:PASSWORD@HOST:PORT/NAME`` [2]_ |
+----------------------+-----------------------------------------------+--------------------------------------------------+
| PostGIS | ``django.contrib.gis.db.backends.postgis`` | ``postgis://USER:PASSWORD@HOST:PORT/NAME`` |
+----------------------+-----------------------------------------------+--------------------------------------------------+
| MSSQL | ``sql_server.pyodbc`` | ``mssql://USER:PASSWORD@HOST:PORT/NAME`` |
+----------------------+-----------------------------------------------+--------------------------------------------------+
| MSSQL [5]_ | ``mssql`` | ``mssqlms://USER:PASSWORD@HOST:PORT/NAME`` |
+----------------------+-----------------------------------------------+--------------------------------------------------+
| MySQL | ``django.db.backends.mysql`` | ``mysql://USER:PASSWORD@HOST:PORT/NAME`` |
+----------------------+-----------------------------------------------+--------------------------------------------------+
| MySQL (GIS) | ``django.contrib.gis.db.backends.mysql`` | ``mysqlgis://USER:PASSWORD@HOST:PORT/NAME`` |
+----------------------+-----------------------------------------------+--------------------------------------------------+
| SQLite | ``django.db.backends.sqlite3`` | ``sqlite:///PATH`` [3]_ |
+----------------------+-----------------------------------------------+--------------------------------------------------+
| SpatiaLite | ``django.contrib.gis.db.backends.spatialite`` | ``spatialite:///PATH`` [3]_ |
+----------------------+-----------------------------------------------+--------------------------------------------------+
| Oracle | ``django.db.backends.oracle`` | ``oracle://USER:PASSWORD@HOST:PORT/NAME`` [4]_ |
+----------------------+-----------------------------------------------+--------------------------------------------------+
| Oracle (GIS) | ``django.contrib.gis.db.backends.oracle`` | ``oraclegis://USER:PASSWORD@HOST:PORT/NAME`` |
+----------------------+-----------------------------------------------+--------------------------------------------------+
| Redshift | ``django_redshift_backend`` | ``redshift://USER:PASSWORD@HOST:PORT/NAME`` |
+----------------------+-----------------------------------------------+--------------------------------------------------+
| CockroachDB | ``django_cockroachdb`` | ``cockroach://USER:PASSWORD@HOST:PORT/NAME`` |
+----------------------+-----------------------------------------------+--------------------------------------------------+
| Timescale [6]_ | ``timescale.db.backends.postgresql`` | ``timescale://USER:PASSWORD@HOST:PORT/NAME`` |
+----------------------+-----------------------------------------------+--------------------------------------------------+
| Timescale (GIS) [6]_ | ``timescale.db.backend.postgis`` | ``timescalegis://USER:PASSWORD@HOST:PORT/NAME`` |
+----------------------+-----------------------------------------------+--------------------------------------------------+

.. [1] The django.db.backends.postgresql backend is named django.db.backends.postgresql_psycopg2 in older releases. For
backwards compatibility, the old name still works in newer versions. (The new name does not work in older versions).
Expand All @@ -127,6 +131,8 @@ URL schema
by ``:`` not by ``/``. Also you can omit ``HOST`` and ``PORT``
and provide a full DSN string or TNS name in ``NAME`` part.
.. [5] Microsoft official `mssql-django <https://github.com/microsoft/mssql-django>`_ adapter.
.. [6] Using the django-timescaledb Package which must be installed.
Contributing
------------
Expand Down
6 changes: 6 additions & 0 deletions dj_database_url.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
urlparse.uses_netloc.append("oraclegis")
urlparse.uses_netloc.append("redshift")
urlparse.uses_netloc.append("cockroach")
urlparse.uses_netloc.append("timescale")
urlparse.uses_netloc.append("timescalegis")

DEFAULT_ENV = "DATABASE_URL"

Expand All @@ -43,6 +45,8 @@
"oraclegis": "django.contrib.gis.db.backends.oracle",
"redshift": "django_redshift_backend",
"cockroach": "django_cockroachdb",
"timescale": "timescale.db.backends.postgresql",
"timescalegis": "timescale.db.backends.postgis",
}

# https://docs.djangoproject.com/en/2.0/releases/2.0/#id1
Expand Down Expand Up @@ -147,6 +151,8 @@ def parse(url, engine=None, conn_max_age=0, ssl_require=False):
"django.db.backends.postgresql_psycopg2",
"django.db.backends.postgresql",
"django_redshift_backend",
"timescale.db.backends.postgresql",
"timescale.db.backends.postgis",
):
options["options"] = "-c search_path={0}".format(options.pop("currentSchema"))

Expand Down
130 changes: 130 additions & 0 deletions test_dj_database_url.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,136 @@ def test_mssqlms_parsing(self):
assert url["OPTIONS"]["driver"] == "ODBC Driver 13 for SQL Server"
assert "currentSchema" not in url["OPTIONS"]

def test_timescale_parsing(self):
url = "timescale://uf07k1i6d8ia0v:[email protected]:5431/d8r82722r2kuvn"
url = dj_database_url.parse(url)

assert url["ENGINE"] == "timescale.db.backends.postgresql"
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

def test_timescale_unix_socket_parsing(self):
url = "timescale://%2Fvar%2Frun%2Fpostgresql/d8r82722r2kuvn"
url = dj_database_url.parse(url)

assert url["ENGINE"] == "timescale.db.backends.postgresql"
assert url["NAME"] == "d8r82722r2kuvn"
assert url["HOST"] == "/var/run/postgresql"
assert url["USER"] == ""
assert url["PASSWORD"] == ""
assert url["PORT"] == ""

url = "timescale://%2FUsers%2Fpostgres%2FRuN/d8r82722r2kuvn"
url = dj_database_url.parse(url)

assert url["ENGINE"] == "timescale.db.backends.postgresql"
assert url["HOST"] == "/Users/postgres/RuN"
assert url["USER"] == ""
assert url["PASSWORD"] == ""
assert url["PORT"] == ""

def test_timescale_ipv6_parsing(self):
url = "timescale://ieRaekei9wilaim7:wegauwhgeuioweg@[2001:db8:1234::1234:5678:90af]:5431/d8r82722r2kuvn"
url = dj_database_url.parse(url)

assert url["ENGINE"] == "timescale.db.backends.postgresql"
assert url["NAME"] == "d8r82722r2kuvn"
assert url["HOST"] == "2001:db8:1234::1234:5678:90af"
assert url["USER"] == "ieRaekei9wilaim7"
assert url["PASSWORD"] == "wegauwhgeuioweg"
assert url["PORT"] == 5431

def test_timescale_search_path_parsing(self):
url = "timescale://uf07k1i6d8ia0v:[email protected]:5431/d8r82722r2kuvn?currentSchema=otherschema"
url = dj_database_url.parse(url)
assert url["ENGINE"] == "timescale.db.backends.postgresql"
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"]["options"] == "-c search_path=otherschema"
assert "currentSchema" not in url["OPTIONS"]

def test_timescale_parsing_with_special_characters(self):
url = "timescale://%23user:%[email protected]:5431/%23database"
url = dj_database_url.parse(url)

assert url["ENGINE"] == "timescale.db.backends.postgresql"
assert url["NAME"] == "#database"
assert url["HOST"] == "ec2-107-21-253-135.compute-1.amazonaws.com"
assert url["USER"] == "#user"
assert url["PASSWORD"] == "#password"
assert url["PORT"] == 5431

def test_timescalegis_parsing(self):
url = "timescalegis://uf07k1i6d8ia0v:[email protected]:5431/d8r82722r2kuvn"
url = dj_database_url.parse(url)

assert url["ENGINE"] == "timescale.db.backends.postgis"
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

def test_timescalegis_unix_socket_parsing(self):
url = "timescalegis://%2Fvar%2Frun%2Fpostgresql/d8r82722r2kuvn"
url = dj_database_url.parse(url)

assert url["ENGINE"] == "timescale.db.backends.postgis"
assert url["NAME"] == "d8r82722r2kuvn"
assert url["HOST"] == "/var/run/postgresql"
assert url["USER"] == ""
assert url["PASSWORD"] == ""
assert url["PORT"] == ""

url = "timescalegis://%2FUsers%2Fpostgres%2FRuN/d8r82722r2kuvn"
url = dj_database_url.parse(url)

assert url["ENGINE"] == "timescale.db.backends.postgis"
assert url["HOST"] == "/Users/postgres/RuN"
assert url["USER"] == ""
assert url["PASSWORD"] == ""
assert url["PORT"] == ""

def test_timescalegis_ipv6_parsing(self):
url = "timescalegis://ieRaekei9wilaim7:wegauwhgeuioweg@[2001:db8:1234::1234:5678:90af]:5431/d8r82722r2kuvn"
url = dj_database_url.parse(url)

assert url["ENGINE"] == "timescale.db.backends.postgis"
assert url["NAME"] == "d8r82722r2kuvn"
assert url["HOST"] == "2001:db8:1234::1234:5678:90af"
assert url["USER"] == "ieRaekei9wilaim7"
assert url["PASSWORD"] == "wegauwhgeuioweg"
assert url["PORT"] == 5431

def test_timescalegis_search_path_parsing(self):
url = "timescalegis://uf07k1i6d8ia0v:[email protected]:5431/d8r82722r2kuvn?currentSchema=otherschema"
url = dj_database_url.parse(url)
assert url["ENGINE"] == "timescale.db.backends.postgis"
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"]["options"] == "-c search_path=otherschema"
assert "currentSchema" not in url["OPTIONS"]

def test_timescalegis_parsing_with_special_characters(self):
url = "timescalegis://%23user:%[email protected]:5431/%23database"
url = dj_database_url.parse(url)

assert url["ENGINE"] == "timescale.db.backends.postgis"
assert url["NAME"] == "#database"
assert url["HOST"] == "ec2-107-21-253-135.compute-1.amazonaws.com"
assert url["USER"] == "#user"
assert url["PASSWORD"] == "#password"
assert url["PORT"] == 5431


if __name__ == "__main__":
unittest.main()

0 comments on commit d60104d

Please sign in to comment.