Compare commits

...

200 commits

Author SHA1 Message Date
dependabot[bot]
b73ad6d9c9
Bump django from 5.2.11 to 5.2.12 (#299)
Some checks failed
test / formatting (push) Has been cancelled
test / typecheck (push) Has been cancelled
test / test (4.2, 3.10) (push) Has been cancelled
test / test (4.2, 3.11) (push) Has been cancelled
test / test (4.2, 3.12) (push) Has been cancelled
test / test (5.2, 3.10) (push) Has been cancelled
test / test (5.2, 3.11) (push) Has been cancelled
test / test (5.2, 3.12) (push) Has been cancelled
test / test (5.2, 3.13) (push) Has been cancelled
test / test (5.2, 3.14) (push) Has been cancelled
test / test (6.0, 3.12) (push) Has been cancelled
test / test (6.0, 3.13) (push) Has been cancelled
test / test (6.0, 3.14) (push) Has been cancelled
Bumps [django](https://github.com/django/django) from 5.2.11 to 5.2.12.
- [Commits](https://github.com/django/django/compare/5.2.11...5.2.12)

---
updated-dependencies:
- dependency-name: django
  dependency-version: 5.2.12
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-05 11:29:02 +00:00
pre-commit-ci[bot]
088b969573
[pre-commit.ci] pre-commit autoupdate (#298)
Some checks failed
test / formatting (push) Has been cancelled
test / typecheck (push) Has been cancelled
test / test (4.2, 3.10) (push) Has been cancelled
test / test (4.2, 3.11) (push) Has been cancelled
test / test (4.2, 3.12) (push) Has been cancelled
test / test (5.2, 3.10) (push) Has been cancelled
test / test (5.2, 3.11) (push) Has been cancelled
test / test (5.2, 3.12) (push) Has been cancelled
test / test (5.2, 3.13) (push) Has been cancelled
test / test (5.2, 3.14) (push) Has been cancelled
test / test (6.0, 3.12) (push) Has been cancelled
test / test (6.0, 3.13) (push) Has been cancelled
test / test (6.0, 3.14) (push) Has been cancelled
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.15.1 → v0.15.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.1...v0.15.4)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-03-03 10:50:11 +00:00
pre-commit-ci[bot]
e77149f799
[pre-commit.ci] pre-commit autoupdate (#297)
Some checks failed
test / formatting (push) Has been cancelled
test / typecheck (push) Has been cancelled
test / test (4.2, 3.10) (push) Has been cancelled
test / test (4.2, 3.11) (push) Has been cancelled
test / test (4.2, 3.12) (push) Has been cancelled
test / test (5.2, 3.10) (push) Has been cancelled
test / test (5.2, 3.11) (push) Has been cancelled
test / test (5.2, 3.12) (push) Has been cancelled
test / test (5.2, 3.13) (push) Has been cancelled
test / test (5.2, 3.14) (push) Has been cancelled
test / test (6.0, 3.12) (push) Has been cancelled
test / test (6.0, 3.13) (push) Has been cancelled
test / test (6.0, 3.14) (push) Has been cancelled
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.15.0 → v0.15.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.0...v0.15.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-02-18 20:23:54 +00:00
Matt Seymour
6beffe6de6 Fix a regression in adding tests/ dir to source package 2026-02-18 20:22:03 +00:00
dependabot[bot]
f9c31305ae
Bump wheel from 0.45.1 to 0.46.2 (#296)
Some checks failed
test / formatting (push) Has been cancelled
test / typecheck (push) Has been cancelled
test / test (4.2, 3.10) (push) Has been cancelled
test / test (4.2, 3.11) (push) Has been cancelled
test / test (4.2, 3.12) (push) Has been cancelled
test / test (5.2, 3.10) (push) Has been cancelled
test / test (5.2, 3.11) (push) Has been cancelled
test / test (5.2, 3.12) (push) Has been cancelled
test / test (5.2, 3.13) (push) Has been cancelled
test / test (5.2, 3.14) (push) Has been cancelled
test / test (6.0, 3.12) (push) Has been cancelled
test / test (6.0, 3.13) (push) Has been cancelled
test / test (6.0, 3.14) (push) Has been cancelled
Bumps [wheel](https://github.com/pypa/wheel) from 0.45.1 to 0.46.2.
- [Release notes](https://github.com/pypa/wheel/releases)
- [Changelog](https://github.com/pypa/wheel/blob/main/docs/news.rst)
- [Commits](https://github.com/pypa/wheel/compare/0.45.1...0.46.2)

---
updated-dependencies:
- dependency-name: wheel
  dependency-version: 0.46.2
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-16 16:20:30 +00:00
dependabot[bot]
5337838ac3
Bump urllib3 from 2.6.2 to 2.6.3 (#295)
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.2 to 2.6.3.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.6.2...2.6.3)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-version: 2.6.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-16 14:37:21 +00:00
dependabot[bot]
6fc366459f
Bump django from 5.2.9 to 5.2.11 (#294)
Bumps [django](https://github.com/django/django) from 5.2.9 to 5.2.11.
- [Commits](https://github.com/django/django/compare/5.2.9...5.2.11)

---
updated-dependencies:
- dependency-name: django
  dependency-version: 5.2.11
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-16 14:33:26 +00:00
dependabot[bot]
19805c97f3
Bump cryptography from 46.0.3 to 46.0.5 (#293)
Bumps [cryptography](https://github.com/pyca/cryptography) from 46.0.3 to 46.0.5.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/46.0.3...46.0.5)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-version: 46.0.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-16 14:31:12 +00:00
Matt Seymour
1b102cd428
Update project URLs in pyproject.toml 2026-02-16 14:28:06 +00:00
pre-commit-ci[bot]
e41afda72b
[pre-commit.ci] pre-commit autoupdate (#291)
Some checks failed
test / formatting (push) Has been cancelled
test / typecheck (push) Has been cancelled
test / test (4.2, 3.10) (push) Has been cancelled
test / test (4.2, 3.11) (push) Has been cancelled
test / test (4.2, 3.12) (push) Has been cancelled
test / test (5.2, 3.10) (push) Has been cancelled
test / test (5.2, 3.11) (push) Has been cancelled
test / test (5.2, 3.12) (push) Has been cancelled
test / test (5.2, 3.13) (push) Has been cancelled
test / test (5.2, 3.14) (push) Has been cancelled
test / test (6.0, 3.12) (push) Has been cancelled
test / test (6.0, 3.13) (push) Has been cancelled
test / test (6.0, 3.14) (push) Has been cancelled
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.14.13 → v0.15.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.13...v0.15.0)
- [github.com/crate-ci/typos: v1.42.0 → v1](https://github.com/crate-ci/typos/compare/v1.42.0...v1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-02-09 19:36:23 +00:00
Matt Seymour
dba6077081
Update .pre-commit-config.yaml to use pinned version numbers. (#289)
Some checks failed
test / formatting (push) Has been cancelled
test / typecheck (push) Has been cancelled
test / test (4.2, 3.10) (push) Has been cancelled
test / test (4.2, 3.11) (push) Has been cancelled
test / test (4.2, 3.12) (push) Has been cancelled
test / test (5.2, 3.10) (push) Has been cancelled
test / test (5.2, 3.11) (push) Has been cancelled
test / test (5.2, 3.12) (push) Has been cancelled
test / test (5.2, 3.13) (push) Has been cancelled
test / test (5.2, 3.14) (push) Has been cancelled
test / test (6.0, 3.12) (push) Has been cancelled
test / test (6.0, 3.13) (push) Has been cancelled
test / test (6.0, 3.14) (push) Has been cancelled
2026-01-19 10:20:19 +00:00
Matt Seymour
e6f4ccc49d Add pytest to dependencies 2026-01-08 10:58:17 +00:00
Aarni Koskela
a87f12185e Enable annotations future; use futuristic annotations 2026-01-08 10:58:17 +00:00
Fábio C. Barrionuevo da Luz
1ca8ccf7db
Add project URLs to pyproject.toml (#287) 2026-01-08 10:12:50 +00:00
Matt Seymour
afad969e94 Merge branch 'ruff' 2026-01-05 10:41:27 +00:00
Matt Seymour
6273af6660 Simplify release workflow by removing created event type. 2026-01-05 10:41:03 +00:00
Matt Seymour
0a59175489 Add formatting step to workflow, integrate ruff check, and update job dependencies 2026-01-05 10:40:44 +00:00
Matt Seymour
51b51776a9
Merge branch 'master' into ruff 2026-01-05 10:29:27 +00:00
Yohaan Narayanan
d8f63c8801
Add tests directory to source distribution (#285) 2026-01-04 16:19:05 -08:00
Matt Seymour
36ec238b03 Update CHANGELOG.md 2026-01-03 09:53:03 +00:00
Matt Seymour
84fd0508ff Switch to modern dict type hints for improved readability and consistency. 2025-12-19 13:14:11 +00:00
Aarni Koskela
2bf2118232 Add crate-ci/typos to pre-commit; fix typos 2025-12-19 12:06:17 +02:00
Aarni Koskela
527962ec04 Switch linting and formatting from flake8+isort+black to ruff 2025-12-19 12:05:33 +02:00
Matt Seymour
992999193d Merge branch 'akx/ci-split' 2025-12-19 09:19:01 +00:00
Matt Seymour
59b3cd6283 Update workflow to be uv compatable 2025-12-19 09:17:33 +00:00
Matt Seymour
37b252e678 Migrate release workflow to release events, update actions, and integrate uv for builds. 2025-12-16 15:54:59 +00:00
Matt Seymour
a01d6b1551 Update project to use uv. 2025-12-16 14:18:42 +00:00
Matt Seymour
989429838a Expand test matrix to support Python 3.14 and Django 6.0, update compatibility exclusions 2025-12-15 16:15:57 +00:00
GabrielBarrantes
438ec393ab
Update license to BSD-3-Clause in setup.py (#279) 2025-12-15 15:58:23 +00:00
pre-commit-ci[bot]
8a3c372deb
[pre-commit.ci] pre-commit autoupdate (#278)
updates:
- [github.com/pycqa/isort: 6.0.1 → 7.0.0](https://github.com/pycqa/isort/compare/6.0.1...7.0.0)
- https://github.com/psf/blackhttps://github.com/psf/black-pre-commit-mirror
- [github.com/psf/black-pre-commit-mirror: 25.9.0 → 25.12.0](https://github.com/psf/black-pre-commit-mirror/compare/25.9.0...25.12.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-12-15 15:56:33 +00:00
pre-commit-ci[bot]
390d524112
[pre-commit.ci] pre-commit autoupdate (#276)
updates:
- [github.com/psf/black: 25.1.0 → 25.9.0](https://github.com/psf/black/compare/25.1.0...25.9.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-09-22 18:45:50 +01:00
Aarni Koskela
58ed5a2276 CI: run typechecks only once, not for each Django/Python version 2025-08-20 12:15:31 +03:00
pre-commit-ci[bot]
7cc1121b29
[pre-commit.ci] pre-commit autoupdate (#270)
updates:
- [github.com/pre-commit/pre-commit-hooks: v5.0.0 → v6.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v5.0.0...v6.0.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-08-11 21:06:04 +01:00
Matt Seymour
0819957676 Allow conn_max_age to accept None for flexibility in configuration 2025-07-11 17:15:17 +01:00
pre-commit-ci[bot]
34170b5aef
[pre-commit.ci] pre-commit autoupdate (#268)
updates:
- [github.com/pycqa/flake8: 7.2.0 → 7.3.0](https://github.com/pycqa/flake8/compare/7.2.0...7.3.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-07-11 16:22:42 +01:00
Matt Seymour
ab20a3aebd
Update setup.py 2025-07-02 10:28:15 +01:00
Matt Seymour
7d47e451a0
Update CHANGELOG.md 2025-07-02 10:28:00 +01:00
Ed Morley
ae2fabf95e
Re-drop dependency on typing_extensions (#269)
Since it was only being used for `TypedDict`, which was added in
Python 3.8 and `dj-database-url` v2.3.0+ only supports Python 3.9+.

See:
https://docs.python.org/3.12/library/typing.html#typing.TypedDict
https://github.com/jazzband/dj-database-url/blob/master/CHANGELOG.md#v230-2024-10-23

Note: This is a repeat of #257, since that PR was lost as part
of merges that occurred during the release of 3.0.0:
https://github.com/jazzband/dj-database-url/pull/257#issuecomment-3023857093
2025-07-01 18:53:01 +01:00
Matt Seymour
7c47649053
Update CHANGELOG.md 2025-06-23 08:54:54 +01:00
Matt Seymour
3e8b69aafc Update python supported version
Pre-empt python 3.14 support.
2025-06-20 20:19:31 +01:00
Matt Seymour
4977c7d0d6
Update package version to 3.0.0 2025-05-18 20:46:58 +01:00
Matt Seymour
a139abcf22
Update project dependencies
Adds typing_extensions
2025-05-18 20:46:51 +01:00
Matt Seymour
e4ccc63ec6
Remove Python 3.9 support from setup.py
Python 3.9 is no longer listed as a supported version in setup.py.
2025-05-09 10:29:33 +01:00
Matt Seymour
9598fb0d85
Add support for Django 5.2 in classifiers
This commit updates the setup.py file to include Django 5.2 in the list of supported framework versions.
2025-05-09 10:29:16 +01:00
Matt Seymour
cdb5d645ec
Update tests 2025-04-30 14:13:38 +01:00
Matt Seymour
15ac40b0a7
Fix pyright issues 2025-04-30 14:08:18 +01:00
pre-commit-ci[bot]
79d929b4e0 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2025-04-30 12:29:23 +00:00
Matt Seymour
c469885ebf
Merge branch 'master' into master 2025-04-30 13:29:15 +01:00
pre-commit-ci[bot]
e90f975526
[pre-commit.ci] pre-commit autoupdate (#262)
updates:
- [github.com/pycqa/isort: 6.0.0 → 6.0.1](https://github.com/pycqa/isort/compare/6.0.0...6.0.1)
- [github.com/pycqa/flake8: 7.1.1 → 7.2.0](https://github.com/pycqa/flake8/compare/7.1.1...7.2.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-04-29 09:39:28 +01:00
Adam Johnson
5f5af49340
Fix type errors (#261)
First, Mypy reported a lot of issues for the tests not including  `-> None` annotations. After adding them, there were lots of issues due to the `url` variables being reassigned from `str` to `DBConfig` objects, fixed by inlining the URLs into the `parse()` calls.
2025-02-16 07:53:52 +00:00
pre-commit-ci[bot]
8501773f73
[pre-commit.ci] pre-commit autoupdate (#259)
updates:
- [github.com/pycqa/isort: 5.13.2 → 6.0.0](https://github.com/pycqa/isort/compare/5.13.2...6.0.0)
- [github.com/psf/black: 24.10.0 → 25.1.0](https://github.com/psf/black/compare/24.10.0...25.1.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-02-03 18:13:35 +00:00
Ed Morley
358746c41d
Drop dependency on typing_extensions (#257)
Since it was only being used for `TypedDict`, which was added in
Python 3.8 and `dj-database-url` v2.3.0+ only supports Python 3.9+.

See:
https://docs.python.org/3.12/library/typing.html#typing.TypedDict
https://github.com/jazzband/dj-database-url/blob/master/CHANGELOG.md#v230-2024-10-23
2024-11-22 12:31:02 +00:00
Matt Seymour
0f8cfdf1c9
Update test.yml 2024-10-23 10:57:17 +01:00
Matt Seymour
3505200b18
Update CHANGELOG.md 2024-10-23 10:55:15 +01:00
Matt Seymour
396344b251
Update setup.py
Bump version 2.3.0 ready for next release.
2024-10-23 10:53:25 +01:00
Matt Seymour
64660c7c5f
Update setup.py
Updated supported versions of python and django
2024-10-23 10:52:32 +01:00
pre-commit-ci[bot]
eff9f27514
[pre-commit.ci] pre-commit autoupdate (#256)
updates:
- [github.com/psf/black: 24.8.0 → 24.10.0](https://github.com/psf/black/compare/24.8.0...24.10.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-10-23 10:50:06 +01:00
Tom Parker-Shemilt
08d9cf0978
Add Python 3.13 and Django 5.1 testing (#255)
* Add Python 3.13 and Django 5.1 testing
* Upgrade to modern actions
2024-10-10 23:00:30 +01:00
Tom Parker-Shemilt
99128b507b
Make config test options not unknown types (#252)
* Make config test options not unknown types
* Run pyright as part of tests
2024-10-10 22:45:31 +01:00
pre-commit-ci[bot]
45ac3bbf89
[pre-commit.ci] pre-commit autoupdate (#254)
updates:
- [github.com/pre-commit/pre-commit-hooks: v4.6.0 → v5.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.6.0...v5.0.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-10-08 14:33:05 -05:00
Lino
8d32603e18
Update CHANGELOG.md
As requested in https://github.com/jazzband/dj-database-url/issues/250
2024-10-03 05:46:26 +02:00
pre-commit-ci[bot]
ed306f37ca
[pre-commit.ci] pre-commit autoupdate (#251)
updates:
- [github.com/psf/black: 24.4.2 → 24.8.0](https://github.com/psf/black/compare/24.4.2...24.8.0)
- [github.com/pycqa/flake8: 7.1.0 → 7.1.1](https://github.com/pycqa/flake8/compare/7.1.0...7.1.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-08-27 09:26:28 +01:00
pre-commit-ci[bot]
3aad9adc71
[pre-commit.ci] pre-commit autoupdate (#249)
updates:
- [github.com/pycqa/flake8: 7.0.0 → 7.1.0](https://github.com/pycqa/flake8/compare/7.0.0...7.1.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-06-19 10:52:48 +01:00
Matt Seymour
51773f3a89
Update setup.py 2024-05-28 13:23:37 +01:00
boxydog
5a91d7a2c7
Update README.rst (#247)
Add `postgresql` as an alternative URL, which works and is in the code.
2024-05-28 13:11:20 +01:00
pre-commit-ci[bot]
cda5b0c3ce
[pre-commit.ci] pre-commit autoupdate (#245)
updates:
- [github.com/psf/black: 24.4.0 → 24.4.2](https://github.com/psf/black/compare/24.4.0...24.4.2)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-05-28 13:07:41 +01:00
pre-commit-ci[bot]
7906f3bf80
[pre-commit.ci] pre-commit autoupdate (#244)
updates:
- [github.com/pre-commit/pre-commit-hooks: v4.5.0 → v4.6.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.5.0...v4.6.0)
- [github.com/psf/black: 24.2.0 → 24.4.0](https://github.com/psf/black/compare/24.2.0...24.4.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-04-22 11:47:10 +01:00
pre-commit-ci[bot]
5106a19129
[pre-commit.ci] pre-commit autoupdate (#242)
updates:
- [github.com/psf/black: 23.12.1 → 24.2.0](https://github.com/psf/black/compare/23.12.1...24.2.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-03-14 16:15:34 +00:00
Alexander Gerbik
b27bd63e82 Generic backend registration 2024-02-29 14:09:10 +01:00
Gert Burger
bcf163e3ab
Check list of schemes to determine if search_path option should be added (#243) 2024-02-20 00:02:06 +00:00
pre-commit-ci[bot]
86f04ef5c6
[pre-commit.ci] pre-commit autoupdate (#241)
updates:
- [github.com/pycqa/flake8: 6.1.0 → 7.0.0](https://github.com/pycqa/flake8/compare/6.1.0...7.0.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-01-09 15:57:45 +00:00
Matt Seymour
fc0dca1b25
Update django 5.0 python compatability (#239) 2023-12-27 16:47:04 +00:00
pre-commit-ci[bot]
ebb86f9b9a
[pre-commit.ci] pre-commit autoupdate (#238)
updates:
- [github.com/psf/black: 23.12.0 → 23.12.1](https://github.com/psf/black/compare/23.12.0...23.12.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-12-27 16:34:07 +00:00
pre-commit-ci[bot]
1412cef6b5
[pre-commit.ci] pre-commit autoupdate (#237)
updates:
- [github.com/pycqa/isort: 5.13.0 → 5.13.2](https://github.com/pycqa/isort/compare/5.13.0...5.13.2)
- [github.com/psf/black: 23.11.0 → 23.12.0](https://github.com/psf/black/compare/23.11.0...23.12.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-12-21 12:50:55 +00:00
pre-commit-ci[bot]
186900d937
[pre-commit.ci] pre-commit autoupdate (#236)
updates:
- [github.com/pycqa/isort: 5.12.0 → 5.13.0](https://github.com/pycqa/isort/compare/5.12.0...5.13.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-12-12 16:25:10 +00:00
Tom Parker-Shemilt
d23a2ca4c5
OPTIONS cannot be None given our code (#232)
As per the notes in https://github.com/jazzband/dj-database-url/issues/218
2023-12-09 09:41:09 +00:00
truongvan
0879f127b9
Enhance Query String Parsing for Server-Side Binding in Django 4.2 with psycopg 3.1.8+ Resolves #234 (#235)
* Refactor parsing of query string to handle int, bool, and text types
2023-12-09 09:39:35 +00:00
pre-commit-ci[bot]
9a74cc5283
[pre-commit.ci] pre-commit autoupdate (#233)
updates:
- [github.com/psf/black: 23.10.1 → 23.11.0](https://github.com/psf/black/compare/23.10.1...23.11.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-11-13 18:26:14 +00:00
Fabian Binz
44bfba5fa2
Add disable_server_side_cursors parameter (#231) 2023-11-08 22:21:29 +00:00
pre-commit-ci[bot]
ee2cb860f5
[pre-commit.ci] pre-commit autoupdate (#229)
updates:
- [github.com/psf/black: 23.10.0 → 23.10.1](https://github.com/psf/black/compare/23.10.0...23.10.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-10-30 17:54:47 +00:00
pre-commit-ci[bot]
9ea2808ed2
[pre-commit.ci] pre-commit autoupdate (#228)
updates:
- [github.com/psf/black: 23.9.1 → 23.10.0](https://github.com/psf/black/compare/23.9.1...23.10.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-10-23 19:24:42 +01:00
pre-commit-ci[bot]
2d46742db5
[pre-commit.ci] pre-commit autoupdate (#227)
updates:
- [github.com/pre-commit/pre-commit-hooks: v4.4.0 → v4.5.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.4.0...v4.5.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-10-10 09:23:03 +01:00
pre-commit-ci[bot]
4a5a75c0f2
[pre-commit.ci] pre-commit autoupdate (#223)
updates:
- [github.com/psf/black: 23.3.0 → 23.9.1](https://github.com/psf/black/compare/23.3.0...23.9.1)
- [github.com/pycqa/flake8: 6.0.0 → 6.1.0](https://github.com/pycqa/flake8/compare/6.0.0...6.1.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-09-26 18:55:22 +01:00
Nikita Sobolev
f600d9089b
Do not leak key loop variable into global namespace (#226)
This is a common problem for module level loops / helpers / etc.

`key` is importable name, it is even imported when using `from dj_database_url import *`. This is clearly not what we want.
2023-09-26 18:52:20 +01:00
Matt Seymour
9292e1fa8a
Update setup.py 2023-08-15 14:11:14 +01:00
Matt Seymour
321a7c207a
Update release.yml
Update version pinning of pypi-publish github action
2023-08-15 13:06:29 +01:00
Matt Seymour
ed68bff3c8
Update CHANGELOG.md 2023-08-15 12:29:47 +01:00
Enrico Stahn
13be1aa9b9
fix: parse options with numerical values as int (#225)
* fix: parse options with numerical values as int

fixes #222

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

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-08-15 12:28:05 +01:00
Matt Seymour
9b0f325910
Bump version 2.0.0 and update CHANGELOG.md 2023-04-27 16:28:38 +01:00
Tom Parker-Shemilt
a7f94c8354
Redo as a package to fix mypy issues (#215)
* Demo the mypy types issue

* Redo as a package

* Update setup.py

---------

Co-authored-by: Matt Seymour <mattaseymour@gmail.com>
2023-04-13 10:59:57 +01:00
pre-commit-ci[bot]
bac900f78a
[pre-commit.ci] pre-commit autoupdate (#216)
updates:
- [github.com/psf/black: 23.1.0 → 23.3.0](https://github.com/psf/black/compare/23.1.0...23.3.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-04-13 10:53:03 +01:00
Michał Górny
84d75e9087
setup.py: Fix project_urls parameter name (#212) 2023-04-02 12:29:31 +01:00
Matt Seymour
2fbcdcbf51
Update change log and setup.py version (1.3.0) 2023-03-27 21:45:16 +01:00
Matt Seymour
14ad3bdc7b
Fixes #209 - Make conn_max_age optional[int] type (#210) 2023-02-14 11:23:46 +00:00
pre-commit-ci[bot]
1cd0d71f56
[pre-commit.ci] pre-commit autoupdate (#208)
updates:
- [github.com/psf/black: 22.12.0 → 23.1.0](https://github.com/psf/black/compare/22.12.0...23.1.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-02-14 11:03:15 +00:00
pre-commit-ci[bot]
acd8d30352
[pre-commit.ci] pre-commit autoupdate (#207)
updates:
- [github.com/pycqa/isort: 5.11.5 → 5.12.0](https://github.com/pycqa/isort/compare/5.11.5...5.12.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-01-31 12:26:52 +00:00
Tom Parker-Shemilt
47ac967679
Throw warning if DATABASE_URL isn't set (#199) 2023-01-30 12:19:02 +00:00
Tom Parker-Shemilt
9c1ebdcda4
isort 5.11.4 is broken (#205)
See https://github.com/PyCQA/isort/issues/2077
2023-01-30 11:55:38 +00:00
Tom Parker-Shemilt
3ef273f086
Avoid duplicating the uses_netloc and schemes lines (#201) 2022-12-30 09:48:07 +00:00
Tom Parker-Shemilt
414c69c122
100% test coverage (#202) 2022-12-30 09:44:45 +00:00
pre-commit-ci[bot]
797fe1e7ce
[pre-commit.ci] pre-commit autoupdate (#203)
updates:
- [github.com/pycqa/isort: v5.11.3 → 5.11.4](https://github.com/pycqa/isort/compare/v5.11.3...5.11.4)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-12-30 09:41:32 +00:00
Tom Parker-Shemilt
698882ea83
Add type hints (#198) 2022-12-21 11:47:41 +00:00
pre-commit-ci[bot]
7755b606ae
[pre-commit.ci] pre-commit autoupdate (#200)
updates:
- [github.com/pycqa/isort: 5.10.1 → v5.11.3](https://github.com/pycqa/isort/compare/5.10.1...v5.11.3)
2022-12-21 11:43:14 +00:00
Tom Parker-Shemilt
e47382d4ab
Better error on missing schema (#196) 2022-12-21 08:19:39 +00:00
Matt Seymour
11854687fa
Update CHANGELOG.md 2022-12-13 09:20:25 +00:00
Matt Seymour
ad0645cb59
Update setup.py 2022-12-13 09:18:16 +00:00
Matt Seymour
7aa7afd764
Update config test_options adding unitest 2022-12-13 09:14:38 +00:00
Matt Seymour
3c1a39b437
Make test_options paramter type None over Dict
Has the potential in the future to be a bug. {} are mutable
in the scope of kwargs of a function.
2022-12-13 09:01:13 +00:00
pre-commit-ci[bot]
b416fa416f
[pre-commit.ci] pre-commit autoupdate (#194)
updates:
- [github.com/psf/black: 22.10.0 → 22.12.0](https://github.com/psf/black/compare/22.10.0...22.12.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Tom Parker-Shemilt <palfrey@tevp.net>
2022-12-13 00:21:42 +00:00
Mike
e0cd4c10b1
Support urlencoding/decoding for hostname (#181)
* Use URL Parse library to decode % encodes

Use URL Parse library for what it's good for: to decode % URL encode strings. Don't do it here -- we're already importing a better library for that!

* Test revision with Google Cloud SQL connector

Add synthetic test correlating to Google Cloud SQL connector socket, to make sure Issue https://github.com/jacobian/dj-database-url/issues/132 is solved

Co-authored-by: jared-hardy <jaredhardy@gmail.com>
2022-12-12 20:18:02 +00:00
Chris Hranj
d2e4719a8d
allow TEST settings to be passed into a db config (#116) 2022-12-12 14:56:50 +00:00
Matt Seymour
e9cb03dd91
Update setup.py
Bump version number 1.1.0
2022-12-12 12:32:24 +00:00
Matt Seymour
3eb28ba1ff
Update CHANGELOG.md 2022-12-12 12:24:55 +00:00
Adam Johnson
f19d4970dc
Add ability to set CONN_HEALTH_CHECKS (#185) 2022-12-11 15:38:29 +00:00
Jair Henrique
ccbcc8f93d
Improve (#190)
* convert list to tuple

* use pathlib to read file data
2022-12-11 15:15:20 +00:00
pre-commit-ci[bot]
b9715d2264
[pre-commit.ci] pre-commit autoupdate (#193)
updates:
- [github.com/pre-commit/pre-commit-hooks: v4.3.0 → v4.4.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.3.0...v4.4.0)
- [github.com/pycqa/flake8: 5.0.4 → 6.0.0](https://github.com/pycqa/flake8/compare/5.0.4...6.0.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-12-11 11:22:44 +00:00
Jair Henrique
ffe20b8abf
Add support to python 3.11 (#189) 2022-12-11 11:14:29 +00:00
Christian Franke
1baa73a4f0
CHANGELOG: Fix date format (#192) 2022-11-10 08:22:59 +00:00
Adam Johnson
9a4a6f3535
Use urlsplit() instead of urlparse() (#184) 2022-10-15 09:24:18 +01:00
pre-commit-ci[bot]
2f5729bb06
[pre-commit.ci] pre-commit autoupdate (#188)
updates:
- [github.com/psf/black: 22.8.0 → 22.10.0](https://github.com/psf/black/compare/22.8.0...22.10.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-10-15 09:13:56 +01:00
Adam Johnson
742d74d091
Support Django 4.1 and remove compatibility code (#183)
* Support Django 4.1 and remove compatibility code

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

for more information, see https://pre-commit.ci

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-09-25 21:49:01 +01:00
pre-commit-ci[bot]
723788a71f
[pre-commit.ci] pre-commit autoupdate (#182)
updates:
- [github.com/psf/black: 22.6.0 → 22.8.0](https://github.com/psf/black/compare/22.6.0...22.8.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-09-06 16:57:25 +01:00
alexanderGerbik
4c284f72b4
Fix linter violation (#179) 2022-08-17 11:35:53 +00:00
pre-commit-ci[bot]
5da1ec5d39
[pre-commit.ci] pre-commit autoupdate (#177)
updates:
- [github.com/pycqa/flake8: 5.0.2 → 5.0.4](https://github.com/pycqa/flake8/compare/5.0.2...5.0.4)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-08-09 20:14:25 +02:00
David D Lowe
3a998ee808
Add project_links to setup.py 2022-08-09 09:32:24 +01:00
pre-commit-ci[bot]
c9e9117d59 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pycqa/flake8: 4.0.1 → 5.0.2](https://github.com/pycqa/flake8/compare/4.0.1...5.0.2)
2022-08-06 08:37:55 +00:00
Lino Helms
91bb95360e Added support for Timescale and Timescale (GIS)
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
2022-08-06 08:37:23 +00:00
Matt Seymour
88b991f60d Remove author_email 2022-07-29 13:21:56 +01:00
Matt Seymour
0f1b9f040d Add twine to release.yml dependencies 2022-07-29 11:09:51 +01:00
Matt Seymour
6fa0da3629 Add release.yml 2022-07-29 10:54:29 +01:00
Matt Seymour
af57107389 Bump version 1.0.0 - setup.py 2022-07-29 10:54:29 +01:00
Matt Seymour
44f5e34c43 Update setup.py 2022-07-29 10:54:29 +01:00
Matt Seymour
8d854dcd78 Update README.rst formatting 2022-07-29 10:48:19 +01:00
pre-commit-ci[bot]
9aa287f28a [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/psf/black: 22.3.0 → 22.6.0](https://github.com/psf/black/compare/22.3.0...22.6.0)
2022-07-11 12:44:20 +00:00
Matt Seymour
66bf061665
Update CHANGELOG.md 2022-06-18 21:44:26 +01:00
pre-commit-ci[bot]
eb804a04d5 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/pre-commit-hooks: v4.2.0 → v4.3.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.2.0...v4.3.0)
2022-06-18 21:36:45 +01:00
Matt Seymour
389febf692
Remove scope name shadow (#169) 2022-05-29 10:33:52 +01:00
Matt Seymour
aba6a67d51
Removes py2.7 urlparse import (#168)
* Fixes #167 - Removes py2.7 urlparse import

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

for more information, see https://pre-commit.ci

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-05-29 10:33:30 +01:00
Matt Seymour
b9cc1e47b7
Make django an optional dependency when running unittests (#166)
Fixes: #165
2022-05-29 10:31:34 +01:00
Daniele Bortoluzzi
931a4e4198
Fix #149 Add support to official Microsoft mssql-django adapter (#150)
* Fix #149 Add support to official Microsoft mssql-django adapter

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

for more information, see https://pre-commit.ci

Co-authored-by: Matt Seymour <mattaseymour@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-05-28 21:18:21 +01:00
Matt Seymour
836740dd81
Update README.rst 2022-05-25 22:23:35 +01:00
Matt Seymour
e589ed03b5
Merge pull request #130 from ParthS007/docs-improvement
Clarify the usage example in the docs
2022-05-24 21:37:49 +01:00
Matt Seymour
e1b62fde7c
Merge pull request #145 from palfrey/cockroachdb
Add CockroachDB to supported database options
2022-05-24 21:36:51 +01:00
Jannis Leidel
dabf94b963
Remove trailing whitespace. 2022-05-24 17:00:20 +02:00
Jannis Leidel
a9c0fe4cb3
Add codecov badge. 2022-05-24 15:29:23 +02:00
Jannis Leidel
18e25d0eaf
Merge pull request #146 from palfrey/actions-pr
Run actions on PRs as well as pushes
2022-05-24 15:27:34 +02:00
Jannis Leidel
759c411b8e
Merge pull request #162 from jazzband/post-transfer-updates
Post transfer updates
2022-05-24 15:27:00 +02:00
Jannis Leidel
5df3b59d12
Add requirements.txt. 2022-05-24 15:16:21 +02:00
Jannis Leidel
683cfb0ee5
Add coverage reporting with codecov. 2022-05-24 15:14:46 +02:00
Jannis Leidel
e83489749e
Minor cleanup post-transfer. 2022-05-24 15:03:57 +02:00
Jannis Leidel
fa2614bd39
Fix badge. 2022-05-24 15:00:28 +02:00
Jannis Leidel
5702e647b0
Don't run tests with Django 4.0 on Python 3.7. 2022-05-24 14:59:01 +02:00
pre-commit-ci[bot]
b7a6e91486 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2022-05-17 21:14:06 +00:00
Matt Seymour
98421fdf85
Merge branch 'master' into cockroachdb 2022-05-17 22:13:39 +01:00
Matt Seymour
36f37c38fc Update map of python django supported versions. 2022-05-17 22:10:15 +01:00
pre-commit-ci[bot]
b96e81d439 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/pre-commit-hooks: v4.0.1 → v4.2.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.0.1...v4.2.0)
2022-05-15 20:55:49 +01:00
Matt Seymour
5616a1bb2b Add isort base config file (.isort.cfg) 2022-05-04 09:56:22 +01:00
Matt Seymour
d99f8aca97 Ref: #156 - Update README.rst to include jazzband logo 2022-05-04 09:52:56 +01:00
pre-commit-ci[bot]
97e986043d [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2022-05-04 09:51:32 +01:00
Matt Seymour
3e87a497f8 Re-format code to black, isort, flake8 2022-05-04 09:51:32 +01:00
Matt Seymour
80770bb243 Add pre-commit-config file
Adds a precommit config file (and .flake8 config) to enable
CI in jazzband organization. Adds basic following checks:
 * Black
 * isort
 * flake8
 * end-of-file-fixer
 * trailing-whitespace
2022-05-04 09:51:32 +01:00
Matt Seymour
6bd09e0661
Merge pull request #154 from mattseymour/jazzbandify
Jazzbandify - Changes required to make the project jazzbandified
2022-05-03 13:27:52 +01:00
Matt Seymour
973b0bcf35 Add flake8 and black config, re-format
* Adds a basic black and flake8 compatible config
* Reformat the code with black using skip string normalization
2022-01-11 12:56:31 +00:00
Matt Seymour
a42c0ba71e Update README.rst with contributing guidelines 2022-01-11 12:39:25 +00:00
Matt Seymour
572877e22a Add jazzband code of conduct file
Add the default code of conduct as found in
jazzband projects.
2022-01-11 12:16:50 +00:00
Matt Seymour
90d36f0fce Add default contributing.md file
Jazzband projects need to include a
CONTRIBUTING.md file. This is the minimum
contribution file required. This should be
expanded in the future.
2022-01-11 12:14:14 +00:00
Matt Seymour
65c62a46c7 Update and reformat license
Update the License formatting to match that
of other jazzband projects.
2022-01-11 12:07:53 +00:00
Tom Parker-Shemilt
daae7cda31 Run actions on PRs as well as pushes 2021-06-07 22:53:57 +01:00
Tom Parker-Shemilt
edba8c1a46 Add CockroachDB to the list of supported DBs 2021-06-07 22:51:50 +01:00
Tom Parker-Shemilt
78d225c069 Add support for CockroachDB 2021-06-07 22:50:57 +01:00
Parth Shandilya
a022bb04a5 Clarify the usage example in the docs 2020-01-21 20:27:09 +01:00
Jacob Kaplan-Moss
1937ed9e61
Merge pull request #125 from giovanism/long_desc
Set long_description to README.rst
2019-12-25 10:53:32 -05:00
Jacob Kaplan-Moss
7546ed77e2
Merge branch 'master' into long_desc 2019-12-25 10:52:28 -05:00
Jacob Kaplan-Moss
306ee25cc0 run tests with python -m unittest discover 2019-12-25 10:50:14 -05:00
Jacob Kaplan-Moss
70ebcfb6db add Django as an explicit dependency
fixes #122
2019-12-25 10:49:48 -05:00
Jacob Kaplan-Moss
dc2a7263c3 🗃 add Python 3.8 / Django 3.0 trove classifiers 2019-12-25 10:40:11 -05:00
Jacob Kaplan-Moss
c01081bd45 📝 point the ci badge at GH actions 2019-12-25 10:39:16 -05:00
Jacob Kaplan-Moss
7db1c32e4d 👷 tweak ci build for readability 2019-12-25 10:33:50 -05:00
Jacob Kaplan-Moss
68b522ea4a 👷 moving to github ci
thanks @jefftriplett for the example -  https://github.com/jefftriplett/python-github-actions-matrix-demo
2019-12-25 10:29:37 -05:00
giovanism
187323a5ec
Set long_description to README.rst 2019-10-03 08:52:54 +07:00
Jacob Kaplan-Moss
332f819b7a
Update LICENSE to reflect multiple contributors 2019-07-24 15:03:07 -04:00
Jacob Kaplan-Moss
acf2909e64
Update the build image; remove outdated sponsorship link 2019-07-24 15:02:26 -04:00
Jacob Kaplan-Moss
eaa67561d4
Merge pull request #111 from adrienbrunet/master
Adding a CHANGELOG
2019-07-24 09:17:58 -04:00
Buğra İşgüzar
004150c17e
Merge pull request #118 from dctalbot/patch-1
Update PostgreSQL Django Backend name
2019-07-12 23:55:18 +03:00
David Talbot
3e7a0e46c7
Footnote 2019-07-12 16:37:18 -04:00
David Talbot
c05d09013f
Update PostgreSQL Django Backend name
More info here:
https://stackoverflow.com/questions/47946856/what-is-the-difference-between-postgres-and-postgresql-psycopg2-as-a-database-en
2019-07-12 15:29:15 -04:00
Ian Stapleton Cordasco
27a56ebfae
Merge pull request #115 from timgraham/django22
Drop testing for Django < 1.11. Add testing for Django 2.2 and Python 3.7.
2019-01-27 08:45:31 -06:00
Tim Graham
d66536836a Add testing for Python 3.7 2019-01-25 15:11:31 -05:00
Tim Graham
2199d7b59d Add testing for Django 2.2 2019-01-25 15:09:50 -05:00
Tim Graham
e143914338 Drop testing for Django < 1.11 2019-01-25 14:46:53 -05:00
Adrien Brunet
754c9a9ebf Adding a CHANGELOG 2018-09-25 18:17:09 +02:00
Kenneth Reitz
43299688f5
Update README.rst 2018-09-17 08:14:27 -04:00
Ian Stapleton Cordasco
7b143e41f4
Merge pull request #109 from timgraham/django21
Add testing for Django 2.1
2018-08-29 14:48:08 -05:00
Tim Graham
1b76ed1e72 Add Django trove classifiers 2018-08-25 20:06:24 -04:00
Tim Graham
6c127a7a05 Add testing for Django 2.1 2018-08-25 20:04:42 -04:00
Ian Stapleton Cordasco
53c3d79d6e
Merge pull request #101 from mintel/psycopg2
fix #96 deprecated postgres backend strings
2018-05-03 10:15:01 -05:00
Jaye Doepke
628335f2d9 Add EXPECTED_POSTGRES_ENGINE 2018-05-02 11:10:20 -05:00
Ian Stapleton Cordasco
b6b6a1f25b
Merge pull request #100 from elohmeier/master
use str port for mssql
2018-05-02 09:39:43 -05:00
Jaye Doepke
8b48e4bd40 fix #96 deprecated postgres backend strings
The backend "django.db.backends.postgresql_psycopg2" has been deprecated
in Django 2.0 in favor of "django.db.backends.postgresql".

https://docs.djangoproject.com/en/2.0/releases/2.0/#id1
2018-03-28 17:14:04 -05:00
Enno Lohmeier
e2f43d97a1 use str port for mssql 2018-02-07 21:33:10 +01:00
21 changed files with 2676 additions and 616 deletions

40
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,40 @@
name: Release
on:
release:
types: [published]
jobs:
build:
if: github.repository == 'jazzband/dj-database-url'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set version from release/tag
id: version
run: |
VERSION=${GITHUB_REF#refs/*/}
VERSION=${VERSION#v}
echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "version=$VERSION" >> $GITHUB_OUTPUT
- uses: astral-sh/setup-uv@v7
with:
python-version: 3.12
- name: Build package
run: |
uv version ${{ env.VERSION }}
uv build
uvx twine check dist/*
- name: Upload packages to Jazzband
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: jazzband
password: ${{ secrets.JAZZBAND_RELEASE_KEY }}
repository_url: https://jazzband.co/projects/dj-database-url/upload

74
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,74 @@
name: test
on: [push, pull_request]
jobs:
formatting:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
python-version: "3.12"
- name: Run ruff
run: uvx ruff check
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
python-version: "3.12"
- name: Run mypy
run: uvx mypy dj_database_url
- name: Run pyright
run: uvx pyright dj_database_url
test:
runs-on: ubuntu-latest
needs: [formatting, typecheck]
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
django-version: ["4.2", "5.2", "6.0"]
exclude:
# django 4.x is not compatible with python 3.13 or higher
- python-version: "3.13"
django-version: "4.2"
- python-version: "3.14"
django-version: "4.2"
# django 6.x is not compatible with python 3.11 or lower
- python-version: "3.10"
django-version: "6.0"
- python-version: "3.11"
django-version: "6.0"
steps:
- uses: actions/checkout@v4
- name: Install uv and set the Python version
uses: astral-sh/setup-uv@v7
with:
python-version: ${{ matrix.python-version }}
activate-environment: 'true'
- name: Install dependencies
run: |
uv pip install "Django~=${{ matrix.django-version }}.0"
- name: Run Tests
run: |
echo "$(python --version) / Django $(django-admin --version)"
uvx coverage run --source=dj_database_url --branch -m unittest discover -v
uvx coverage report
uvx coverage xml
- uses: codecov/codecov-action@v4

1
.gitignore vendored
View file

@ -53,3 +53,4 @@ docs/_build/
# Virtualenv
env/
.vscode/

21
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,21 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.4
hooks:
- id: ruff-check
args:
- --fix
- id: ruff-format
args:
- --quiet
- repo: https://github.com/crate-ci/typos
rev: v1
hooks:
- id: typos

View file

@ -1,9 +0,0 @@
language: python
python:
- 2.7
- 3.2
- 3.3
- 3.4
- 3.5
- 3.6
script: make test

167
CHANGELOG.md Normal file
View file

@ -0,0 +1,167 @@
# CHANGELOG
## v3.1.0 (2026-01-03)
* Add support for Django 6.0
* Update CI structure.
* Migrate to UV for dependency management and builds.
* Python >3.10 support.
## v3.0.1 (2025-07-01)
* Drop dependency on `typing_extensions`.
## v3.0.0 (2025-05-18)
> Bumping to version 3; changes to code do break some API compatibility.
* Implement a new decorator registry pattern to implement checks on database connection string.
* You can now support and implement your own database strings by extending the @register functionality.
* Update supported python versions and django versions.
## v2.3.0 (2024-10-23)
* Remove Python 3.8 support.
* Remove Django 3 support.
* Add python 3.13 support.
* Add Django 5.1 to the testing library.
## v2.2.0 (2024-05-28)
* Add disable_server_side_cursors parameter
* Enhance Query String Parsing for Server-Side Binding in Django 4.2 with psycopg 3.1.8+
* Update django 5.0 python compatibility by @mattseymour in #239
* Improved internals
* Improved documentation
## v2.1.0 (2023-08-15)
* Add value to int parsing when deconstructing url string.
## v2.0.0 (2023-04-27)
* Update project setup such that we now install it as a package.
_Notes_: while this does not alter the underlying application code, we are bumping to
2.0 incase there are unforeseen knock-on use-case issues.
## v1.3.0 (2023-03-27)
* Cosmetic changes to the generation of schemes.
* Bump isort version - 5.11.5.
* raise a warning message if `database_url` is not set.
* `CONN_MAX_AGE` fix type - `Optional[int]`.
## v1.2.0 (2022-12-13)
* Add the ability to add test databases.
* Improve url parsing and encoding.
* Fix missing parameter `conn_health_check` in check function.
## v1.1.0 (2022-12-12)
* Option for connection health checks parameter.
* Update supported version python 3.11.
* Code changes, various improvements.
* Add project links to `setup.py`.
## v1.0.0 (2022-06-18)
Initial release of code, dj-database-urls is now part of [Jazzband](https://jazzband.co/).
* Add support for cockroachdb.
* Add support for the official MSSQL connector.
* Update License to be compatible with Jazzband.
* Remove support for Python < 3.5 including Python 2.7
* Update source code to Black format.
* Update CI using pre-commit
## v0.5.0 (2018-03-01)
- Use str port for mssql
- Added license
- Add mssql to readme
- Add mssql support using pyodbc
- Fix RST schemas
- Django expects Oracle Ports as strings
- Fix IPv6 address parsing
- Add testing for Python 3.6
- Revert "Add setup.cfg for wheel support"
- added option of postgis backend to also add path parsing. (test added also)
- Support schema definition for redshift
- add redshift support
- Add testing for Python 3.5
- Drop testing for Python 2.6
- Fixes issue with unix file paths being turned to lower case
- add Redis support
- Added SpatiaLite in README.rst
## v0.4.1 (2016-04-06)
- Enable CA providing for MySQL URIs
- Update Readme
- Update trove classifiers
- Updated setup.py description
## v0.4.0 (2016-02-04)
- Update readme
- Fix for python3
- Handle search path config in connection url for postgres
- Add tox config to ease testing against multiple Python versions
- Simplified the querystring parse logic
- Cleaned up querystring parsing
- supports database options
- Added tests for CONN_MAX_AGE
- Added documentation for conn_max_age
- Add in optional support for CONN_MAX_AGE
- Support special characters in user, password and name fields
- Add oracle support
- Added support for percent-encoded postgres paths
- Fixed test_cleardb_parsing test
- Enable automated testing with Python 3.4
- Add URL schema examples to README
- Added support for python mysql-connector
## v0.3.0 (2014-03-10)
- Add `.gitignore` file
- Remove `.pyc` file
- Remove travis-ci unsupported python versions (Travis CI supports Python versions 2.6, 2.7, 3.2 and 3.3)
- Fix cleardb test
- Add `setup.cfg` for wheel support
- Add trove classifiers for python versions
- Replace Python 3.1 with Python 3.3
- Add MySQL (GIS) support
- Ability to set different engine
## v0.2.2 (2013-07-17)
- Added spatialite to uses_netloc too
- Added spatialite backend
- Replacing tab with spaces
- Handling special case of `sqlite://:memory:`
- Empty sqlite path will now use a :memory: database
- Fixing test to actually use the result of the parse
- Adding in tests to ensure sqlite in-memory databases work
- Fixed a too-short title underline
- Added :target: attribute to Travis status image in README
- Added docs for default argument to config
- Add "pgsql" as a PostgreSQL URL scheme.
- Add support for blank fields (Django expects '' not None)
- fixed url
## v0.2.1 (2012-06-19)
- Add python3 support
- Adding travis status and tests
- Adding test environment variables
- Adding test for cleardb
- Remove query strings from name
- Adding postgres tests
- Adding tests
- refactor scheme lookup
- RedHat's OpenShift platform uses the 'postgresql' scheme
- Registered postgis URL scheme
- Added `postgis://` url scheme
- Use `get()` on `os.environ` instead of an `if`.
## v0.2.0 (2012-05-30)
- Fix parse(s)
## v0.1.4 (2012-05-30)
- Add defaults for env
- Set the DATABASES dict rather than assigning to it
## v0.1.3 (2012-05-01)
- Add a note to README on supported databases
- Add support for SQLite
- Clean dependencies
## v0.1.2 (2012-04-30)
- Update readme
- Refactor config and use a new parse function
## v0.1.1 (2012-04-30) First release
🐍 ✨

46
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,46 @@
# Code of Conduct
As contributors and maintainers of the Jazzband projects, and in the interest of
fostering an open and welcoming community, we pledge to respect all people who
contribute through reporting issues, posting feature requests, updating documentation,
submitting pull requests or patches, and other activities.
We are committed to making participation in the Jazzband a harassment-free experience
for everyone, regardless of the level of experience, gender, gender identity and
expression, sexual orientation, disability, personal appearance, body size, race,
ethnicity, age, religion, or nationality.
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery
- Personal attacks
- Trolling or insulting/derogatory comments
- Public or private harassment
- Publishing other's private information, such as physical or electronic addresses,
without explicit permission
- Other unethical or unprofessional conduct
The Jazzband roadies have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are not
aligned to this Code of Conduct, or to ban temporarily or permanently any contributor
for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
By adopting this Code of Conduct, the roadies commit themselves to fairly and
consistently applying these principles to every aspect of managing the jazzband
projects. Roadies who do not follow or enforce the Code of Conduct may be permanently
removed from the Jazzband roadies.
This code of conduct applies both within project spaces and in public spaces when an
individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
contacting the roadies at `roadies@jazzband.co`. All complaints will be reviewed and
investigated and will result in a response that is deemed necessary and appropriate to
the circumstances. Roadies are obligated to maintain confidentiality with regard to the
reporter of an incident.
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version
1.3.0, available at [https://contributor-covenant.org/version/1/3/0/][version]
[homepage]: https://contributor-covenant.org
[version]: https://contributor-covenant.org/version/1/3/0/

7
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,7 @@
[![Jazzband](https://jazzband.co/static/img/jazzband.svg)](https://jazzband.co/)
This is a [Jazzband](https://jazzband.co/) project. By contributing you agree to abide by the [Contributor Code of Conduct](https://jazzband.co/about/conduct) and follow the [guidelines](https://jazzband.co/about/guidelines).
Please see the
[full contributing documentation](https://django-debug-toolbar.readthedocs.io/en/stable/contributing.html)
for more help.

29
LICENSE
View file

@ -1,8 +1,27 @@
Copyright (c) 2014, Kenneth Reitz
Copyright (c) Kenneth Reitz & individual contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of Django nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -1,2 +0,0 @@
test:
python test_dj_database_url.py

View file

@ -1,8 +1,15 @@
DJ-Database-URL
~~~~~~~~~~~~~~~
.. image:: https://secure.travis-ci.org/kennethreitz/dj-database-url.svg?branch=master
:target: http://travis-ci.org/kennethreitz/dj-database-url
.. image:: https://jazzband.co/static/img/badge.png
:target: https://jazzband.co/
:alt: Jazzband
.. image:: https://github.com/jazzband/dj-database-url/actions/workflows/test.yml/badge.svg
:target: https://github.com/jazzband/dj-database-url/actions/workflows/test.yml
.. image:: https://codecov.io/gh/jazzband/dj-database-url/branch/master/graph/badge.svg?token=7srBUpszOa
:target: https://codecov.io/gh/jazzband/dj-database-url
This simple Django utility allows you to utilize the
`12factor <http://www.12factor.net/backing-services>`_ inspired
@ -15,76 +22,256 @@ also a `conn_max_age` argument to easily enable Django's connection pool.
If you'd rather not use an environment variable, you can pass a URL in directly
instead to ``dj_database_url.parse``.
Supported Databases
-------------------
Support currently exists for PostgreSQL, PostGIS, MySQL, MySQL (GIS),
Oracle, Oracle (GIS), Redshift, and SQLite.
Installation
------------
Installation is simple::
Installation is simple:
.. code-block:: console
$ pip install dj-database-url
Usage
-----
Configure your database in ``settings.py`` from ``DATABASE_URL``::
1. If ``DATABASES`` is already defined:
- Configure your database in ``settings.py`` from ``DATABASE_URL``:
.. code-block:: python
import dj_database_url
DATABASES['default'] = dj_database_url.config(
conn_max_age=600,
conn_health_checks=True,
)
- Provide a default:
.. code-block:: python
DATABASES['default'] = dj_database_url.config(
default='postgres://...',
conn_max_age=600,
conn_health_checks=True,
)
- Parse an arbitrary Database URL:
.. code-block:: python
DATABASES['default'] = dj_database_url.parse(
'postgres://...',
conn_max_age=600,
conn_health_checks=True,
)
2. If ``DATABASES`` is not defined:
- Configure your database in ``settings.py`` from ``DATABASE_URL``:
.. code-block:: python
import dj_database_url
DATABASES = {
'default': dj_database_url.config(
conn_max_age=600,
conn_health_checks=True,
),
}
- You can provide a default, used if the ``DATABASE_URL`` setting is not defined:
.. code-block:: python
DATABASES = {
'default': dj_database_url.config(
default='postgres://...',
conn_max_age=600,
conn_health_checks=True,
)
}
- Parse an arbitrary Database URL:
.. code-block:: python
DATABASES = {
'default': dj_database_url.parse(
'postgres://...',
conn_max_age=600,
conn_health_checks=True,
)
}
``conn_max_age`` sets the |CONN_MAX_AGE setting|__, which tells Django to
persist database connections between requests, up to the given lifetime in
seconds. If you do not provide a value, it will follow Djangos default of
``0``. Setting it is recommended for performance.
.. |CONN_MAX_AGE setting| replace:: ``CONN_MAX_AGE`` setting
__ https://docs.djangoproject.com/en/stable/ref/settings/#conn-max-age
``conn_health_checks`` sets the |CONN_HEALTH_CHECKS setting|__ (new in Django
4.1), which tells Django to check a persisted connection still works at the
start of each request. If you do not provide a value, it will follow Djangos
default of ``False``. Enabling it is recommended if you set a non-zero
``conn_max_age``.
.. |CONN_HEALTH_CHECKS setting| replace:: ``CONN_HEALTH_CHECKS`` setting
__ https://docs.djangoproject.com/en/stable/ref/settings/#conn-health-checks
Strings passed to `dj_database_url` must be valid URLs; in
particular, special characters must be url-encoded. The following url will raise
a `ValueError`:
.. code-block:: plaintext
postgres://user:p#ssword!@localhost/foobar
and should instead be passed as:
.. code-block:: plaintext
postgres://user:p%23ssword!@localhost/foobar
`TEST <https://docs.djangoproject.com/en/stable/ref/settings/#test>`_ settings can be configured using the ``test_options`` attribute::
DATABASES['default'] = dj_database_url.config(default='postgres://...', test_options={'NAME': 'mytestdatabase'})
Supported Databases
-------------------
Support currently exists for PostgreSQL, PostGIS, MySQL, MySQL (GIS),
Oracle, Oracle (GIS), Redshift, CockroachDB, Timescale, Timescale (GIS) and SQLite.
If you want to use
some non-default backends, you need to register them first:
.. code-block:: python
import dj_database_url
DATABASES['default'] = dj_database_url.config(conn_max_age=600)
# registration should be performed only once
dj_database_url.register("mysql-connector", "mysql.connector.django")
Provide a default::
assert dj_database_url.parse("mysql-connector://user:password@host:port/db-name") == {
"ENGINE": "mysql.connector.django",
# ...other connection params
}
DATABASES['default'] = dj_database_url.config(default='postgres://...')
Some backends need further config adjustments (e.g. oracle and mssql
expect ``PORT`` to be a string). For such cases you can provide a
post-processing function to ``register()`` (note that ``register()`` is
used as a **decorator(!)** in this case):
Parse an arbitrary Database URL::
.. code-block:: python
DATABASES['default'] = dj_database_url.parse('postgres://...', conn_max_age=600)
import dj_database_url
The ``conn_max_age`` attribute is the lifetime of a database connection in seconds
and is available in Django 1.6+. If you do not set a value, it will default to ``0``
which is Django's historical behavior of using a new database connection on each
request. Use ``None`` for unlimited persistent connections.
@dj_database_url.register("mssql", "sql_server.pyodbc")
def stringify_port(config):
config["PORT"] = str(config["PORT"])
@dj_database_url.register("redshift", "django_redshift_backend")
def apply_current_schema(config):
options = config["OPTIONS"]
schema = options.pop("currentSchema", None)
if schema:
options["options"] = f"-c search_path={schema}"
@dj_database_url.register("snowflake", "django_snowflake")
def adjust_snowflake_config(config):
config.pop("PORT", None)
config["ACCOUNT"] = config.pop("HOST")
name, _, schema = config["NAME"].partition("/")
if schema:
config["SCHEMA"] = schema
config["NAME"] = name
options = config.get("OPTIONS", {})
warehouse = options.pop("warehouse", None)
if warehouse:
config["WAREHOUSE"] = warehouse
role = options.pop("role", None)
if role:
config["ROLE"] = role
URL schema
----------
+-------------+-----------------------------------------------+--------------------------------------------------+
| Engine | Django Backend | URL |
+=============+===============================================+==================================================+
| PostgreSQL | ``django.db.backends.postgresql_psycopg2`` | ``postgres://USER:PASSWORD@HOST:PORT/NAME`` [1]_ |
+-------------+-----------------------------------------------+--------------------------------------------------+
| PostGIS | ``django.contrib.gis.db.backends.postgis`` | ``postgis://USER:PASSWORD@HOST:PORT/NAME`` |
+-------------+-----------------------------------------------+--------------------------------------------------+
| MSSQL | ``sql_server.pyodbc`` | ``mssql://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`` [2]_ |
+-------------+-----------------------------------------------+--------------------------------------------------+
| SpatiaLite | ``django.contrib.gis.db.backends.spatialite`` | ``spatialite:///PATH`` [2]_ |
+-------------+-----------------------------------------------+--------------------------------------------------+
| Oracle | ``django.db.backends.oracle`` | ``oracle://USER:PASSWORD@HOST:PORT/NAME`` [3]_ |
+-------------+-----------------------------------------------+--------------------------------------------------+
| Oracle (GIS)| ``django.contrib.gis.db.backends.oracle`` | ``oraclegis://USER:PASSWORD@HOST:PORT/NAME`` |
+-------------+-----------------------------------------------+--------------------------------------------------+
| Redshift | ``django_redshift_backend`` | ``redshift://USER:PASSWORD@HOST:PORT/NAME`` |
+-------------+-----------------------------------------------+--------------------------------------------------+
+----------------------+-----------------------------------------------+--------------------------------------------------+
| Engine | Django Backend | URL |
+======================+===============================================+==================================================+
| PostgreSQL | ``django.db.backends.postgresql`` [1]_ | ``postgres://USER:PASSWORD@HOST:PORT/NAME`` [2]_ |
| | | ``postgresql://USER:PASSWORD@HOST:PORT/NAME`` |
+----------------------+-----------------------------------------------+--------------------------------------------------+
| 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`` [2]_ |
+----------------------+-----------------------------------------------+--------------------------------------------------+
| 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] With PostgreSQL, you can also use unix domain socket paths with
.. [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).
.. [2] With PostgreSQL or CloudSQL, you can also use unix domain socket paths with
`percent encoding <http://www.postgresql.org/docs/9.2/interactive/libpq-connect.html#AEN38162>`_:
``postgres://%2Fvar%2Flib%2Fpostgresql/dbname``.
.. [2] SQLite connects to file based databases. The same URL format is used, omitting
``postgres://%2Fvar%2Flib%2Fpostgresql/dbname``
``mysql://uf07k1i6d8ia0v@%2fcloudsql%2fproject%3alocation%3ainstance/dbname``
.. [3] SQLite connects to file based databases. The same URL format is used, omitting
the hostname, and using the "file" portion as the filename of the database.
This has the effect of four slashes being present for an absolute file path:
``sqlite:////full/path/to/your/database/file.sqlite``.
.. [3] Note that when connecting to Oracle the URL isn't in the form you may know
.. [4] Note that when connecting to Oracle the URL isn't in the form you may know
from using other Oracle tools (like SQLPlus) i.e. user and password are separated
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
------------
We welcome contributions to this project. Projects can take two forms:
1. Raising issues or helping others through the github issue tracker.
2. Contributing code.
Raising Issues or helping others:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When submitting an issue or helping other remember you are talking to humans who have feelings, jobs and lives of their
own. Be nice, be kind, be polite. Remember english may not be someone first language, if you do not understand or
something is not clear be polite and re-ask/ re-word.
Contributing code:
^^^^^^^^^^^^^^^^^^
* Before writing code be sure to check existing PR's and issues in the tracker.
* Write code to the pylint spec.
* Large or wide sweeping changes will take longer, and may face more scrutiny than smaller confined changes.
* Code should be pass `black` and `flake8` validation.

View file

@ -1,144 +0,0 @@
# -*- coding: utf-8 -*-
import os
try:
import urlparse
except ImportError:
import urllib.parse as urlparse
# Register database schemes in URLs.
urlparse.uses_netloc.append('postgres')
urlparse.uses_netloc.append('postgresql')
urlparse.uses_netloc.append('pgsql')
urlparse.uses_netloc.append('postgis')
urlparse.uses_netloc.append('mysql')
urlparse.uses_netloc.append('mysql2')
urlparse.uses_netloc.append('mysqlgis')
urlparse.uses_netloc.append('mysql-connector')
urlparse.uses_netloc.append('mssql')
urlparse.uses_netloc.append('spatialite')
urlparse.uses_netloc.append('sqlite')
urlparse.uses_netloc.append('oracle')
urlparse.uses_netloc.append('oraclegis')
urlparse.uses_netloc.append('redshift')
DEFAULT_ENV = 'DATABASE_URL'
SCHEMES = {
'postgres': 'django.db.backends.postgresql_psycopg2',
'postgresql': 'django.db.backends.postgresql_psycopg2',
'pgsql': 'django.db.backends.postgresql_psycopg2',
'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',
'mssql': 'sql_server.pyodbc',
'spatialite': 'django.contrib.gis.db.backends.spatialite',
'sqlite': 'django.db.backends.sqlite3',
'oracle': 'django.db.backends.oracle',
'oraclegis': 'django.contrib.gis.db.backends.oracle',
'redshift': 'django_redshift_backend',
}
def config(env=DEFAULT_ENV, default=None, engine=None, conn_max_age=0, ssl_require=False):
"""Returns configured DATABASE dictionary from DATABASE_URL."""
config = {}
s = os.environ.get(env, default)
if s:
config = parse(s, engine, conn_max_age, ssl_require)
return config
def parse(url, engine=None, conn_max_age=0, ssl_require=False):
"""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:'
}
# note: no other settings are required for sqlite
# otherwise parse the url as normal
config = {}
url = urlparse.urlparse(url)
# 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)
# 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.
hostname = url.hostname or ''
if '%2f' in hostname.lower():
# 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', '/')
# Lookup specified engine.
engine = SCHEMES[url.scheme] if engine is None else engine
port = (str(url.port) if url.port and engine == SCHEMES['oracle']
else url.port)
# 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,
'PORT': port or '',
'CONN_MAX_AGE': conn_max_age,
})
# Pass the query string into OPTIONS.
options = {}
for key, values in query.items():
if url.scheme == 'mysql' and key == 'ssl-ca':
options['ssl'] = {'ca': values[-1]}
continue
options[key] = values[-1]
if ssl_require:
options['sslmode'] = 'require'
# Support for Postgres Schema URLs
if 'currentSchema' in options and engine in (
'django.contrib.gis.db.backends.postgis',
'django.db.backends.postgresql_psycopg2',
'django_redshift_backend',
):
options['options'] = '-c search_path={0}'.format(options.pop('currentSchema'))
if options:
config['OPTIONS'] = options
if engine:
config['ENGINE'] = engine
return config

253
dj_database_url/__init__.py Normal file
View file

@ -0,0 +1,253 @@
import logging
import os
import urllib.parse as urlparse
from collections.abc import Callable
from typing import Any, TypedDict
DEFAULT_ENV = "DATABASE_URL"
ENGINE_SCHEMES: dict[str, "Engine"] = {}
# From https://docs.djangoproject.com/en/stable/ref/settings/#databases
class DBConfig(TypedDict, total=False):
ATOMIC_REQUESTS: bool
AUTOCOMMIT: bool
CONN_MAX_AGE: int | None
CONN_HEALTH_CHECKS: bool
DISABLE_SERVER_SIDE_CURSORS: bool
ENGINE: str
HOST: str
NAME: str
OPTIONS: dict[str, Any]
PASSWORD: str
PORT: str | int
TEST: dict[str, Any]
TIME_ZONE: str
USER: str
PostprocessCallable = Callable[[DBConfig], None]
OptionType = int | str | bool
class ParseError(ValueError):
def __str__(self) -> str:
return (
"This string is not a valid url, possibly because some of its parts"
" is not properly urllib.parse.quote()'ed."
)
class UnknownSchemeError(ValueError):
def __init__(self, scheme: str):
self.scheme = scheme
def __str__(self) -> str:
schemes = ", ".join(sorted(ENGINE_SCHEMES.keys()))
return (
f"Scheme '{self.scheme}://' is unknown."
" Did you forget to register custom backend?"
f" Following schemes have registered backends: {schemes}."
)
def default_postprocess(parsed_config: DBConfig) -> None:
pass
class Engine:
def __init__(
self,
backend: str,
postprocess: PostprocessCallable = default_postprocess,
):
self.backend = backend
self.postprocess = postprocess
def register(
scheme: str, backend: str
) -> Callable[[PostprocessCallable], PostprocessCallable]:
engine = Engine(backend)
if scheme not in ENGINE_SCHEMES:
urlparse.uses_netloc.append(scheme)
ENGINE_SCHEMES[scheme] = engine
def inner(func: PostprocessCallable) -> PostprocessCallable:
engine.postprocess = func
return func
return inner
register("spatialite", "django.contrib.gis.db.backends.spatialite")
register("mysql-connector", "mysql.connector.django")
register("mysqlgis", "django.contrib.gis.db.backends.mysql")
register("oraclegis", "django.contrib.gis.db.backends.oracle")
register("cockroach", "django_cockroachdb")
@register("sqlite", "django.db.backends.sqlite3")
def default_to_in_memory_db(parsed_config: DBConfig) -> None:
# mimic sqlalchemy behaviour
if not parsed_config.get("NAME"):
parsed_config["NAME"] = ":memory:"
@register("oracle", "django.db.backends.oracle")
@register("mssqlms", "mssql")
@register("mssql", "sql_server.pyodbc")
def stringify_port(parsed_config: DBConfig) -> None:
parsed_config["PORT"] = str(parsed_config.get("PORT", ""))
@register("mysql", "django.db.backends.mysql")
@register("mysql2", "django.db.backends.mysql")
def apply_ssl_ca(parsed_config: DBConfig) -> None:
options = parsed_config.get("OPTIONS", {})
ca = options.pop("ssl-ca", None)
if ca:
options["ssl"] = {"ca": ca}
@register("postgres", "django.db.backends.postgresql")
@register("postgresql", "django.db.backends.postgresql")
@register("pgsql", "django.db.backends.postgresql")
@register("postgis", "django.contrib.gis.db.backends.postgis")
@register("redshift", "django_redshift_backend")
@register("timescale", "timescale.db.backends.postgresql")
@register("timescalegis", "timescale.db.backends.postgis")
def apply_current_schema(parsed_config: DBConfig) -> None:
options = parsed_config.get("OPTIONS", {})
schema = options.pop("currentSchema", None)
if schema:
options["options"] = f"-c search_path={schema}"
def config(
env: str = DEFAULT_ENV,
default: str | None = None,
engine: str | None = None,
conn_max_age: int | None = 0,
conn_health_checks: bool = False,
disable_server_side_cursors: bool = False,
ssl_require: bool = False,
test_options: dict[str, Any] | None = None,
) -> DBConfig:
"""Returns configured DATABASE dictionary from DATABASE_URL."""
s = os.environ.get(env, default)
if s is None:
logging.warning(
"No %s environment variable set, and so no databases setup", env
)
if s:
return parse(
s,
engine,
conn_max_age,
conn_health_checks,
disable_server_side_cursors,
ssl_require,
test_options,
)
return {}
def parse(
url: str,
engine: str | None = None,
conn_max_age: int | None = 0,
conn_health_checks: bool = False,
disable_server_side_cursors: bool = False,
ssl_require: bool = False,
test_options: dict[str, Any] | None = None,
) -> DBConfig:
"""Parses a database URL and returns configured DATABASE dictionary."""
settings = _convert_to_settings(
engine,
conn_max_age,
conn_health_checks,
disable_server_side_cursors,
ssl_require,
test_options,
)
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": ENGINE_SCHEMES["sqlite"].backend, "NAME": ":memory:"}
# note: no other settings are required for sqlite
try:
split_result = urlparse.urlsplit(url)
engine_obj = ENGINE_SCHEMES.get(split_result.scheme)
if engine_obj is None:
raise UnknownSchemeError(split_result.scheme)
path = split_result.path[1:]
query = urlparse.parse_qs(split_result.query)
options = {k: _parse_option_values(v) for k, v in query.items()}
parsed_config: DBConfig = {
"ENGINE": engine_obj.backend,
"USER": urlparse.unquote(split_result.username or ""),
"PASSWORD": urlparse.unquote(split_result.password or ""),
"HOST": urlparse.unquote(split_result.hostname or ""),
"PORT": split_result.port or "",
"NAME": urlparse.unquote(path),
"OPTIONS": options,
}
except UnknownSchemeError:
raise
except ValueError:
raise ParseError() from None
# Guarantee that config has options, possibly empty, when postprocess() is called
assert isinstance(parsed_config["OPTIONS"], dict)
engine_obj.postprocess(parsed_config)
# Update the final config with any settings passed in explicitly.
parsed_config["OPTIONS"].update(settings.pop("OPTIONS", {}))
parsed_config.update(settings)
if not parsed_config["OPTIONS"]:
parsed_config.pop("OPTIONS")
return parsed_config
def _parse_option_values(values: list[str]) -> OptionType | list[OptionType]:
parsed_values = [_parse_value(v) for v in values]
return parsed_values[0] if len(parsed_values) == 1 else parsed_values
def _parse_value(value: str) -> OptionType:
if value.isdigit():
return int(value)
if value.lower() in ("true", "false"):
return value.lower() == "true"
return value
def _convert_to_settings(
engine: str | None,
conn_max_age: int | None,
conn_health_checks: bool,
disable_server_side_cursors: bool,
ssl_require: bool,
test_options: dict[str, Any] | None,
) -> DBConfig:
settings: DBConfig = {
"CONN_MAX_AGE": conn_max_age,
"CONN_HEALTH_CHECKS": conn_health_checks,
"DISABLE_SERVER_SIDE_CURSORS": disable_server_side_cursors,
}
if engine:
settings["ENGINE"] = engine
if ssl_require:
settings["OPTIONS"] = {}
settings["OPTIONS"]["sslmode"] = "require"
if test_options:
settings["TEST"] = test_options
return settings

0
dj_database_url/py.typed Normal file
View file

View file

@ -1 +0,0 @@
include LICENSE

79
pyproject.toml Normal file
View file

@ -0,0 +1,79 @@
[project]
name = "dj-database-url"
version = "0.0.0"
description = "Use Database URLs in your Django Application."
authors = [
{ name = "Jazzband community" }
]
readme = "README.rst"
requires-python = ">=3.10"
license = "BSD-3-Clause"
license-files = ["LICENSE"]
dependencies = [
"django>=4.2",
]
classifiers = [
"Environment :: Web Environment",
"Framework :: Django",
"Framework :: Django :: 4.2",
"Framework :: Django :: 5.2",
"Framework :: Django :: 6",
"Intended Audience :: Developers",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
"Topic :: Software Development :: Libraries :: Python Modules",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
]
[project.urls]
Homepage = "https://jazzband.co/projects/dj-database-url"
Changelog = "https://github.com/jazzband/dj-database-url/blob/master/CHANGELOG.md"
Funding = "https://psfmember.org/civicrm/contribute/transact/?reset=1&id=34"
Bug = "https://github.com/jazzband/dj-database-url/issues"
[build-system]
requires = ["uv_build>=0.9.17,<0.10.0"]
build-backend = "uv_build"
[tool.uv.build-backend]
module-name = "dj_database_url"
module-root = ""
source-include = ["dj_database_url/py.typed", "tests/**"]
[tool.ruff]
line-length = 88
[tool.ruff.lint]
extend-select = ["B", "I"]
[tool.ruff.format]
quote-style = "preserve"
[tool.mypy]
show_error_codes=true
disallow_untyped_defs=true
disallow_untyped_calls=true
warn_redundant_casts=true
[tool.pyright]
typeCheckingMode = "strict"
[dependency-groups]
dev = [
"coverage>=7.13.0",
"mypy>=1.19.1",
"pyright>=1.1.407",
"pytest>=9.0.2",
"ruff>=0.14.10",
"setuptools>=80.9.0",
"twine>=6.2.0",
"wheel>=0.45.1",
]

View file

@ -1,89 +0,0 @@
# -*- coding: utf-8 -*-
"""
dj-database-url
~~~~~~~~~~~~~~~
.. image:: https://secure.travis-ci.org/kennethreitz/dj-database-url.png?branch=master
:target: http://travis-ci.org/kennethreitz/dj-database-url
This simple Django utility allows you to utilize the
`12factor <http://www.12factor.net/backing-services>`_ inspired
``DATABASE_URL`` environment variable to configure your Django application.
The ``dj_database_url.config`` method returns a Django database connection
dictionary, populated with all the data specified in your URL. There is
also a `conn_max_age` argument to easily enable Django's connection pool.
If you'd rather not use an environment variable, you can pass a URL in directly
instead to ``dj_database_url.parse``.
Supported Databases
-------------------
Support currently exists for PostgreSQL, PostGIS, MySQL, MySQL (GIS),
Oracle, Oracle (GIS), and SQLite.
Installation
------------
Installation is simple::
$ pip install dj-database-url
Usage
-----
Configure your database in ``settings.py`` from ``DATABASE_URL``::
import dj_database_url
DATABASES['default'] = dj_database_url.config(conn_max_age=600, ssl_require=True)
Provide a default::
DATABASES['default'] = dj_database_url.config(default='postgres://...'}
Parse an arbitrary Database URL::
DATABASES['default'] = dj_database_url.parse('postgres://...', conn_max_age=600)
The ``conn_max_age`` attribute is the lifetime of a database connection in seconds
and is available in Django 1.6+. If you do not set a value, it will default to ``0``
which is Django's historical behavior of using a new database connection on each
request. Use ``None`` for unlimited persistent connections.
"""
from setuptools import setup
setup(
name='dj-database-url',
version='0.5.0',
url='https://github.com/kennethreitz/dj-database-url',
license='BSD',
author='Kenneth Reitz',
author_email='me@kennethreitz.com',
description='Use Database URLs in your Django Application.',
long_description=__doc__,
py_modules=['dj_database_url'],
zip_safe=False,
include_package_data=True,
platforms='any',
classifiers=[
'Environment :: Web Environment',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
'Topic :: Software Development :: Libraries :: Python Modules',
'Programming Language :: Python',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
]
)

View file

@ -1,320 +0,0 @@
# -*- coding: utf-8 -*-
#!/usr/bin/env python
import os
import unittest
import dj_database_url
POSTGIS_URL = 'postgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn'
class DatabaseTestSuite(unittest.TestCase):
def test_postgres_parsing(self):
url = 'postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn'
url = dj_database_url.parse(url)
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
def test_postgres_unix_socket_parsing(self):
url = 'postgres://%2Fvar%2Frun%2Fpostgresql/d8r82722r2kuvn'
url = dj_database_url.parse(url)
assert url['ENGINE'] == 'django.db.backends.postgresql_psycopg2'
assert url['NAME'] == 'd8r82722r2kuvn'
assert url['HOST'] == '/var/run/postgresql'
assert url['USER'] == ''
assert url['PASSWORD'] == ''
assert url['PORT'] == ''
url = 'postgres://%2FUsers%2Fpostgres%2FRuN/d8r82722r2kuvn'
url = dj_database_url.parse(url)
assert url['ENGINE'] == 'django.db.backends.postgresql_psycopg2'
assert url['HOST'] == '/Users/postgres/RuN'
assert url['USER'] == ''
assert url['PASSWORD'] == ''
assert url['PORT'] == ''
def test_ipv6_parsing(self):
url = 'postgres://ieRaekei9wilaim7:wegauwhgeuioweg@[2001:db8:1234::1234:5678:90af]:5431/d8r82722r2kuvn'
url = dj_database_url.parse(url)
assert url['ENGINE'] == 'django.db.backends.postgresql_psycopg2'
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_postgres_search_path_parsing(self):
url = 'postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?currentSchema=otherschema'
url = dj_database_url.parse(url)
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']['options'] == '-c search_path=otherschema'
assert 'currentSchema' not in url['OPTIONS']
def test_postgres_parsing_with_special_characters(self):
url = 'postgres://%23user:%23password@ec2-107-21-253-135.compute-1.amazonaws.com:5431/%23database'
url = dj_database_url.parse(url)
assert url['ENGINE'] == 'django.db.backends.postgresql_psycopg2'
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_postgis_parsing(self):
url = 'postgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn'
url = dj_database_url.parse(url)
assert url['ENGINE'] == 'django.contrib.gis.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_postgis_search_path_parsing(self):
url = 'postgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?currentSchema=otherschema'
url = dj_database_url.parse(url)
assert url['ENGINE'] == 'django.contrib.gis.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_mysql_gis_parsing(self):
url = 'mysqlgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn'
url = dj_database_url.parse(url)
assert url['ENGINE'] == 'django.contrib.gis.db.backends.mysql'
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_mysql_connector_parsing(self):
url = 'mysql-connector://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn'
url = dj_database_url.parse(url)
assert url['ENGINE'] == 'mysql.connector.django'
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_cleardb_parsing(self):
url = 'mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true'
url = dj_database_url.parse(url)
assert url['ENGINE'] == 'django.db.backends.mysql'
assert url['NAME'] == 'heroku_97681db3eff7580'
assert url['HOST'] == 'us-cdbr-east.cleardb.com'
assert url['USER'] == 'bea6eb025ca0d8'
assert url['PASSWORD'] == '69772142'
assert url['PORT'] == ''
def test_database_url(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'
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
def test_empty_sqlite_url(self):
url = 'sqlite://'
url = dj_database_url.parse(url)
assert url['ENGINE'] == 'django.db.backends.sqlite3'
assert url['NAME'] == ':memory:'
def test_memory_sqlite_url(self):
url = 'sqlite://:memory:'
url = dj_database_url.parse(url)
assert url['ENGINE'] == 'django.db.backends.sqlite3'
assert url['NAME'] == ':memory:'
def test_parse_engine_setting(self):
engine = 'django_mysqlpool.backends.mysqlpool'
url = 'mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true'
url = dj_database_url.parse(url, engine)
assert url['ENGINE'] == engine
def test_config_engine_setting(self):
engine = 'django_mysqlpool.backends.mysqlpool'
os.environ['DATABASE_URL'] = 'mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true'
url = dj_database_url.config(engine=engine)
assert url['ENGINE'] == engine
def test_parse_conn_max_age_setting(self):
conn_max_age = 600
url = 'mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true'
url = dj_database_url.parse(url, conn_max_age=conn_max_age)
assert url['CONN_MAX_AGE'] == conn_max_age
def test_config_conn_max_age_setting(self):
conn_max_age = 600
os.environ['DATABASE_URL'] = 'mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true'
url = dj_database_url.config(conn_max_age=conn_max_age)
assert url['CONN_MAX_AGE'] == conn_max_age
def test_database_url_with_options(self):
# 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'
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'
}
# 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
def test_mysql_database_url_with_sslca_options(self):
os.environ['DATABASE_URL'] = 'mysql://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:3306/d8r82722r2kuvn?ssl-ca=rds-combined-ca-bundle.pem'
url = dj_database_url.config()
assert url['ENGINE'] == 'django.db.backends.mysql'
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'] == 3306
assert url['OPTIONS'] == {
'ssl': {
'ca': 'rds-combined-ca-bundle.pem'
}
}
# Test empty options
os.environ['DATABASE_URL'] = 'mysql://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:3306/d8r82722r2kuvn?'
url = dj_database_url.config()
assert 'OPTIONS' not in url
def test_oracle_parsing(self):
url = 'oracle://scott:tiger@oraclehost:1521/hr'
url = dj_database_url.parse(url)
assert url['ENGINE'] == 'django.db.backends.oracle'
assert url['NAME'] == 'hr'
assert url['HOST'] == 'oraclehost'
assert url['USER'] == 'scott'
assert url['PASSWORD'] == 'tiger'
assert url['PORT'] == '1521'
def test_oracle_gis_parsing(self):
url = 'oraclegis://scott:tiger@oraclehost:1521/hr'
url = dj_database_url.parse(url)
assert url['ENGINE'] == 'django.contrib.gis.db.backends.oracle'
assert url['NAME'] == 'hr'
assert url['HOST'] == 'oraclehost'
assert url['USER'] == 'scott'
assert url['PASSWORD'] == 'tiger'
assert url['PORT'] == 1521
def test_oracle_dsn_parsing(self):
url = (
'oracle://scott:tiger@/'
'(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)'
'(HOST=oraclehost)(PORT=1521)))'
'(CONNECT_DATA=(SID=hr)))'
)
url = dj_database_url.parse(url)
assert url['ENGINE'] == 'django.db.backends.oracle'
assert url['USER'] == 'scott'
assert url['PASSWORD'] == 'tiger'
assert url['HOST'] == ''
assert url['PORT'] == ''
dsn = (
'(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)'
'(HOST=oraclehost)(PORT=1521)))'
'(CONNECT_DATA=(SID=hr)))'
)
assert url['NAME'] == dsn
def test_oracle_tns_parsing(self):
url = 'oracle://scott:tiger@/tnsname'
url = dj_database_url.parse(url)
assert url['ENGINE'] == 'django.db.backends.oracle'
assert url['USER'] == 'scott'
assert url['PASSWORD'] == 'tiger'
assert url['NAME'] == 'tnsname'
assert url['HOST'] == ''
assert url['PORT'] == ''
def test_redshift_parsing(self):
url = 'redshift://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5439/d8r82722r2kuvn?currentSchema=otherschema'
url = dj_database_url.parse(url)
assert url['ENGINE'] == 'django_redshift_backend'
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'] == 5439
assert url['OPTIONS']['options'] == '-c search_path=otherschema'
assert 'currentSchema' not in url['OPTIONS']
def test_mssql_parsing(self):
url = 'mssql://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com/d8r82722r2kuvn?driver=ODBC Driver 13 for SQL Server'
url = dj_database_url.parse(url)
assert url['ENGINE'] == 'sql_server.pyodbc'
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'] == ''
assert url['OPTIONS']['driver'] == 'ODBC Driver 13 for SQL Server'
assert 'currentSchema' not in url['OPTIONS']
if __name__ == '__main__':
unittest.main()

0
tests/__init__.py Normal file
View file

View file

@ -0,0 +1,698 @@
# pyright: reportTypedDictNotRequiredAccess=false
import os
import re
import unittest
from unittest import mock
from urllib.parse import uses_netloc
import dj_database_url
POSTGIS_URL = "postgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
class DatabaseTestSuite(unittest.TestCase):
def test_postgres_parsing(self) -> None:
url = dj_database_url.parse(
"postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
)
assert url["ENGINE"] == "django.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_postgres_unix_socket_parsing(self) -> None:
url = dj_database_url.parse(
"postgres://%2Fvar%2Frun%2Fpostgresql/d8r82722r2kuvn"
)
assert url["ENGINE"] == "django.db.backends.postgresql"
assert url["NAME"] == "d8r82722r2kuvn"
assert url["HOST"] == "/var/run/postgresql"
assert url["USER"] == ""
assert url["PASSWORD"] == ""
assert url["PORT"] == ""
url = dj_database_url.parse(
"postgres://%2FUsers%2Fpostgres%2FRuN/d8r82722r2kuvn"
)
assert url["ENGINE"] == "django.db.backends.postgresql"
assert url["HOST"] == "/Users/postgres/RuN"
assert url["USER"] == ""
assert url["PASSWORD"] == ""
assert url["PORT"] == ""
def test_postgres_google_cloud_parsing(self) -> None:
url = dj_database_url.parse(
"postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@%2Fcloudsql%2Fproject_id%3Aregion%3Ainstance_id/d8r82722r2kuvn"
)
assert url["ENGINE"] == "django.db.backends.postgresql"
assert url["NAME"] == "d8r82722r2kuvn"
assert url["HOST"] == "/cloudsql/project_id:region:instance_id"
assert url["USER"] == "uf07k1i6d8ia0v"
assert url["PASSWORD"] == "wegauwhgeuioweg"
assert url["PORT"] == ""
def test_ipv6_parsing(self) -> None:
url = dj_database_url.parse(
"postgres://ieRaekei9wilaim7:wegauwhgeuioweg@[2001:db8:1234::1234:5678:90af]:5431/d8r82722r2kuvn"
)
assert url["ENGINE"] == "django.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_postgres_search_path_parsing(self) -> None:
url = dj_database_url.parse(
"postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?currentSchema=otherschema"
)
assert url["ENGINE"] == "django.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_postgres_parsing_with_special_characters(self) -> None:
url = dj_database_url.parse(
"postgres://%23user:%23password@ec2-107-21-253-135.compute-1.amazonaws.com:5431/%23database"
)
assert url["ENGINE"] == "django.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_postgres_parsing_with_int_bool_str_query_string(self) -> None:
url = dj_database_url.parse(
"postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?server_side_binding=true&timeout=20&service=my_service&passfile=.my_pgpass"
)
assert url["ENGINE"] == "django.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"]["server_side_binding"] is True
assert url["OPTIONS"]["timeout"] == 20
assert url["OPTIONS"]["service"] == "my_service"
assert url["OPTIONS"]["passfile"] == ".my_pgpass"
def test_postgis_parsing(self) -> None:
url = dj_database_url.parse(
"postgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
)
assert url["ENGINE"] == "django.contrib.gis.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_postgis_search_path_parsing(self) -> None:
url = dj_database_url.parse(
"postgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?currentSchema=otherschema"
)
assert url["ENGINE"] == "django.contrib.gis.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_mysql_gis_parsing(self) -> None:
url = dj_database_url.parse(
"mysqlgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
)
assert url["ENGINE"] == "django.contrib.gis.db.backends.mysql"
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_mysql_connector_parsing(self) -> None:
url = dj_database_url.parse(
"mysql-connector://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
)
assert url["ENGINE"] == "mysql.connector.django"
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_config_test_options(self) -> None:
with mock.patch.dict(
os.environ,
{
"DATABASE_URL": "postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?"
},
):
test_db_config = {
'NAME': 'mytestdatabase',
}
url = dj_database_url.config(test_options=test_db_config)
assert url['TEST']['NAME'] == 'mytestdatabase'
def test_cleardb_parsing(self) -> None:
url = dj_database_url.parse(
"mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true"
)
assert url["ENGINE"] == "django.db.backends.mysql"
assert url["NAME"] == "heroku_97681db3eff7580"
assert url["HOST"] == "us-cdbr-east.cleardb.com"
assert url["USER"] == "bea6eb025ca0d8"
assert url["PASSWORD"] == "69772142"
assert url["PORT"] == ""
def test_database_url(self) -> None:
with mock.patch.dict(os.environ, clear=True):
a = dj_database_url.config()
assert not a
with mock.patch.dict(
os.environ,
{
"DATABASE_URL": "postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
},
):
url = dj_database_url.config()
assert url["ENGINE"] == "django.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_empty_sqlite_url(self) -> None:
url = dj_database_url.parse("sqlite://")
assert url["ENGINE"] == "django.db.backends.sqlite3"
assert url["NAME"] == ":memory:"
def test_memory_sqlite_url(self) -> None:
url = dj_database_url.parse("sqlite://:memory:")
assert url["ENGINE"] == "django.db.backends.sqlite3"
assert url["NAME"] == ":memory:"
def test_sqlite_relative_url(self) -> None:
url = "sqlite:///db.sqlite3"
config = dj_database_url.parse(url)
assert config["ENGINE"] == "django.db.backends.sqlite3"
assert config["NAME"] == "db.sqlite3"
def test_sqlite_absolute_url(self) -> None:
# 4 slashes are needed:
# two are part of scheme
# one separates host:port from path
# and the fourth goes to "NAME" value
url = "sqlite:////db.sqlite3"
config = dj_database_url.parse(url)
assert config["ENGINE"] == "django.db.backends.sqlite3"
assert config["NAME"] == "/db.sqlite3"
def test_parse_engine_setting(self) -> None:
engine = "django_mysqlpool.backends.mysqlpool"
url = dj_database_url.parse(
"mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true",
engine,
)
assert url["ENGINE"] == engine
def test_config_engine_setting(self) -> None:
engine = "django_mysqlpool.backends.mysqlpool"
with mock.patch.dict(
os.environ,
{
"DATABASE_URL": "mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true"
},
):
url = dj_database_url.config(engine=engine)
assert url["ENGINE"] == engine
def test_parse_conn_max_age_setting(self) -> None:
conn_max_age = 600
url = dj_database_url.parse(
"mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true",
conn_max_age=conn_max_age,
)
assert url["CONN_MAX_AGE"] == conn_max_age
def test_config_conn_max_age_setting_none(self) -> None:
conn_max_age = None
with mock.patch.dict(
os.environ,
{
"DATABASE_URL": "mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true"
},
):
url = dj_database_url.config(conn_max_age=conn_max_age)
assert url["CONN_MAX_AGE"] == conn_max_age
def test_database_url_with_options(self) -> None:
# Test full options
with mock.patch.dict(
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"
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",
}
# Test empty options
with mock.patch.dict(
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
def test_mysql_database_url_with_sslca_options(self) -> None:
with mock.patch.dict(
os.environ,
{
"DATABASE_URL": "mysql://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:3306/d8r82722r2kuvn?ssl-ca=rds-combined-ca-bundle.pem"
},
):
url = dj_database_url.config()
assert url["ENGINE"] == "django.db.backends.mysql"
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"] == 3306
assert url["OPTIONS"] == {"ssl": {"ca": "rds-combined-ca-bundle.pem"}}
# Test empty options
with mock.patch.dict(
os.environ,
{
"DATABASE_URL": "mysql://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:3306/d8r82722r2kuvn?"
},
):
url = dj_database_url.config()
assert "OPTIONS" not in url
def test_oracle_parsing(self) -> None:
url = dj_database_url.parse("oracle://scott:tiger@oraclehost:1521/hr")
assert url["ENGINE"] == "django.db.backends.oracle"
assert url["NAME"] == "hr"
assert url["HOST"] == "oraclehost"
assert url["USER"] == "scott"
assert url["PASSWORD"] == "tiger"
assert url["PORT"] == "1521"
def test_oracle_gis_parsing(self) -> None:
url = dj_database_url.parse("oraclegis://scott:tiger@oraclehost:1521/hr")
assert url["ENGINE"] == "django.contrib.gis.db.backends.oracle"
assert url["NAME"] == "hr"
assert url["HOST"] == "oraclehost"
assert url["USER"] == "scott"
assert url["PASSWORD"] == "tiger"
assert url["PORT"] == 1521
def test_oracle_dsn_parsing(self) -> None:
url = dj_database_url.parse(
"oracle://scott:tiger@/"
"(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)"
"(HOST=oraclehost)(PORT=1521)))"
"(CONNECT_DATA=(SID=hr)))"
)
assert url["ENGINE"] == "django.db.backends.oracle"
assert url["USER"] == "scott"
assert url["PASSWORD"] == "tiger"
assert url["HOST"] == ""
assert url["PORT"] == ""
dsn = (
"(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)"
"(HOST=oraclehost)(PORT=1521)))"
"(CONNECT_DATA=(SID=hr)))"
)
assert url["NAME"] == dsn
def test_oracle_tns_parsing(self) -> None:
url = dj_database_url.parse("oracle://scott:tiger@/tnsname")
assert url["ENGINE"] == "django.db.backends.oracle"
assert url["USER"] == "scott"
assert url["PASSWORD"] == "tiger"
assert url["NAME"] == "tnsname"
assert url["HOST"] == ""
assert url["PORT"] == ""
def test_redshift_parsing(self) -> None:
url = dj_database_url.parse(
"redshift://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5439/d8r82722r2kuvn?currentSchema=otherschema"
)
assert url["ENGINE"] == "django_redshift_backend"
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"] == 5439
assert url["OPTIONS"]["options"] == "-c search_path=otherschema"
assert "currentSchema" not in url["OPTIONS"]
def test_mssql_parsing(self) -> None:
url = dj_database_url.parse(
"mssql://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com/d8r82722r2kuvn?driver=ODBC Driver 13 for SQL Server"
)
assert url["ENGINE"] == "sql_server.pyodbc"
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"] == ""
assert url["OPTIONS"]["driver"] == "ODBC Driver 13 for SQL Server"
assert "currentSchema" not in url["OPTIONS"]
def test_mssql_instance_port_parsing(self) -> None:
url = dj_database_url.parse(
"mssql://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com\\insnsnss:12345/d8r82722r2kuvn?driver=ODBC Driver 13 for SQL Server"
)
assert url["ENGINE"] == "sql_server.pyodbc"
assert url["NAME"] == "d8r82722r2kuvn"
assert url["HOST"] == "ec2-107-21-253-135.compute-1.amazonaws.com\\insnsnss"
assert url["USER"] == "uf07k1i6d8ia0v"
assert url["PASSWORD"] == "wegauwhgeuioweg"
assert url["PORT"] == "12345"
assert url["OPTIONS"]["driver"] == "ODBC Driver 13 for SQL Server"
assert "currentSchema" not in url["OPTIONS"]
def test_cockroach(self) -> None:
url = dj_database_url.parse(
"cockroach://testuser:testpass@testhost:26257/cockroach?sslmode=verify-full&sslrootcert=/certs/ca.crt&sslcert=/certs/client.myprojectuser.crt&sslkey=/certs/client.myprojectuser.key"
)
assert url['ENGINE'] == 'django_cockroachdb'
assert url['NAME'] == 'cockroach'
assert url['HOST'] == 'testhost'
assert url['USER'] == 'testuser'
assert url['PASSWORD'] == 'testpass'
assert url['PORT'] == 26257
assert url['OPTIONS']['sslmode'] == 'verify-full'
assert url['OPTIONS']['sslrootcert'] == '/certs/ca.crt'
assert url['OPTIONS']['sslcert'] == '/certs/client.myprojectuser.crt'
assert url['OPTIONS']['sslkey'] == '/certs/client.myprojectuser.key'
def test_mssqlms_parsing(self) -> None:
url = dj_database_url.parse(
"mssqlms://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com/d8r82722r2kuvn?driver=ODBC Driver 13 for SQL Server"
)
assert url["ENGINE"] == "mssql"
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"] == ""
assert url["OPTIONS"]["driver"] == "ODBC Driver 13 for SQL Server"
assert "currentSchema" not in url["OPTIONS"]
def test_timescale_parsing(self) -> None:
url = dj_database_url.parse(
"timescale://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
)
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) -> None:
url = dj_database_url.parse(
"timescale://%2Fvar%2Frun%2Fpostgresql/d8r82722r2kuvn"
)
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 = dj_database_url.parse(
"timescale://%2FUsers%2Fpostgres%2FRuN/d8r82722r2kuvn"
)
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) -> None:
url = dj_database_url.parse(
"timescale://ieRaekei9wilaim7:wegauwhgeuioweg@[2001:db8:1234::1234:5678:90af]:5431/d8r82722r2kuvn"
)
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) -> None:
url = dj_database_url.parse(
"timescale://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?currentSchema=otherschema"
)
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) -> None:
url = dj_database_url.parse(
"timescale://%23user:%23password@ec2-107-21-253-135.compute-1.amazonaws.com:5431/%23database"
)
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) -> None:
url = dj_database_url.parse(
"timescalegis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
)
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) -> None:
url = dj_database_url.parse(
"timescalegis://%2Fvar%2Frun%2Fpostgresql/d8r82722r2kuvn"
)
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 = dj_database_url.parse(
"timescalegis://%2FUsers%2Fpostgres%2FRuN/d8r82722r2kuvn"
)
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) -> None:
url = dj_database_url.parse(
"timescalegis://ieRaekei9wilaim7:wegauwhgeuioweg@[2001:db8:1234::1234:5678:90af]:5431/d8r82722r2kuvn"
)
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) -> None:
url = dj_database_url.parse(
"timescalegis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?currentSchema=otherschema"
)
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) -> None:
url = dj_database_url.parse(
"timescalegis://%23user:%23password@ec2-107-21-253-135.compute-1.amazonaws.com:5431/%23database"
)
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
def test_persistent_connection_variables(self) -> None:
url = dj_database_url.parse(
"sqlite://myfile.db", conn_max_age=600, conn_health_checks=True
)
assert url["CONN_MAX_AGE"] == 600
assert url["CONN_HEALTH_CHECKS"] is True
def test_sqlite_memory_persistent_connection_variables(self) -> None:
# memory sqlite ignores connection.close(), so persistent connection
# variables arent required
url = dj_database_url.parse(
"sqlite://:memory:", conn_max_age=600, conn_health_checks=True
)
assert "CONN_MAX_AGE" not in url
assert "CONN_HEALTH_CHECKS" not in url
@mock.patch.dict(
os.environ,
{"DATABASE_URL": "postgres://user:password@instance.amazonaws.com:5431/d8r8?"},
)
def test_persistent_connection_variables_config(self) -> None:
url = dj_database_url.config(conn_max_age=600, conn_health_checks=True)
assert url["CONN_MAX_AGE"] == 600
assert url["CONN_HEALTH_CHECKS"] is True
def test_no_env_variable(self) -> None:
with self.assertLogs() as cm:
with mock.patch.dict(os.environ, clear=True):
url = dj_database_url.config()
assert url == {}, url
assert cm.output == [
'WARNING:root:No DATABASE_URL environment variable set, and so no databases setup'
], cm.output
def test_credentials_unquoted__raise_value_error(self) -> None:
expected_message = (
"This string is not a valid url, possibly because some of its parts "
r"is not properly urllib.parse.quote()'ed."
)
with self.assertRaisesRegex(ValueError, re.escape(expected_message)):
dj_database_url.parse("postgres://user:passw#ord!@localhost/foobar")
def test_credentials_quoted__ok(self) -> None:
url = "postgres://user%40domain:p%23ssword!@localhost/foobar"
config = dj_database_url.parse(url)
assert config["USER"] == "user@domain"
assert config["PASSWORD"] == "p#ssword!"
def test_unknown_scheme__raise_value_error(self) -> None:
expected_message = (
"Scheme 'unknown-scheme://' is unknown. "
"Did you forget to register custom backend? Following schemes have registered backends:"
)
with self.assertRaisesRegex(ValueError, re.escape(expected_message)):
dj_database_url.parse("unknown-scheme://user:password@localhost/foobar")
def test_register_multiple_times__no_duplicates_in_uses_netloc(self) -> None:
# make sure that when register() function is misused,
# it won't pollute urllib.parse.uses_netloc list with duplicates.
# Otherwise, it might cause performance issue if some code assumes that
# that list is short and performs linear search on it.
dj_database_url.register("django.contrib.db.backends.bag_end", "bag-end")
dj_database_url.register("django.contrib.db.backends.bag_end", "bag-end")
assert len(uses_netloc) == len(set(uses_netloc))
@mock.patch.dict(
os.environ,
{"DATABASE_URL": "postgres://user:password@instance.amazonaws.com:5431/d8r8?"},
)
def test_ssl_require(self) -> None:
url = dj_database_url.config(ssl_require=True)
assert url["OPTIONS"] == {'sslmode': 'require'}
def test_options_int_values(self) -> None:
"""Ensure that options with integer values are parsed correctly."""
url = dj_database_url.parse(
"mysql://user:pw@127.0.0.1:15036/db?connect_timeout=3"
)
assert url["OPTIONS"] == {'connect_timeout': 3}
@mock.patch.dict(
os.environ,
{"DATABASE_URL": "postgres://user:password@instance.amazonaws.com:5431/d8r8?"},
)
def test_server_side_cursors__config(self) -> None:
url = dj_database_url.config(disable_server_side_cursors=True)
assert url["DISABLE_SERVER_SIDE_CURSORS"] is True
if __name__ == "__main__":
unittest.main()

1033
uv.lock Normal file

File diff suppressed because it is too large Load diff