setting helper for django to represent databases, caches and email settings via a single string
Project description
django-service-urls
django-service-urls is a setting helper for django to represent databases, caches, email, storages and task backends via a single string.
This work is based on dj-database-url and https://github.com/django/django/pull/8562.
Example
Original config:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydb',
'HOST': 'localhost',
'PORT': 5432,
'USER': 'myuser',
'PASSWORD': 'mypasswd',
'OPTIONS': {
'pool': {
'min_size': 2,
'max_size': 10,
},
'sslmode': 'require',
},
'CONN_MAX_AGE': 300,
},
}
CACHES = {
'default': {
'BACKEND' : 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
'OPTIONS': {
'timeout': 300,
'key_prefix': 'myapp',
},
},
}
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
HOST = 'localhost'
PORT = 2525
HOST_USER = ''
HOST_PASSWORD = ''
USE_TLS = True
USE_SSL = False
SSL_CERTFILE = '/etc/ssl/cert'
SSL_KEYFILE = '/etc/ssl/key'
TIMEOUT = 600
USE_LOCALTIME = False
STORAGES = {
"default": {
"BACKEND": "storages.backends.s3.S3Storage",
"OPTIONS": {
"bucket_name": "mybucket",
"region_name": "us-east-1",
},
},
}
TASKS = {
"default": {
"BACKEND": "django_tasks.backends.database.DatabaseBackend",
"OPTIONS": {},
},
}
Replace with:
DATABASES = {
'default': os.environ.get('DATABASE_DEFAULT', 'postgres://myuser:mypasswd@localhost:5432/mydb?pool.min_size=2&pool.max_size=10&sslmode=require#CONN_MAX_AGE=300'),
}
CACHES = {
'default': os.environ.get('CACHE_DEFAULT', 'memcached://127.0.0.1:11211?timeout=300&key_prefix=myapp'),
}
EMAIL_BACKEND = os.environ.get('EMAIL_BACKEND', 'smtps://localhost:2525?ssl_certfile=/etc/ssl/cert&ssl_keyfile=/etc/ssl/key&timeout=600')
STORAGES = {
'default': os.environ.get('STORAGE_DEFAULT', 's3://?bucket_name=mybucket®ion_name=us-east-1'),
}
TASKS = {
'default': os.environ.get('TASKS_DEFAULT', 'database+dt://'),
}
Advanced Features (Nested dictionaries, lists, fragments, booleans and integers)
django-service-urls supports nested dictionaries using dot notation, lists using repeated parameters, and URL fragments for top-level configuration keys.
Boolean values are automatically recognized: true, false, t, f, yes, no, y, n (case-insensitive).
Integer values are automatically converted: 123, 0, 999 → int type.
# Nested options with dot notation
'postgres://user:pass@host/db?pool.min_size=2&pool.max_size=10&sslmode=require'
# → OPTIONS: {
# 'pool': {'min_size': 2, 'max_size': 10},
# 'sslmode': 'require',
# }
# Lists with repeated parameters
'postgres://user:pass@host/db?hosts=host1&hosts=host2&hosts=host3'
# → OPTIONS: {
# 'hosts': ['host1', 'host2', 'host3'],
# }
# Combined: nested structure with lists
'postgres://user:pass@host/db?pool.hosts=host1&pool.hosts=host2&pool.ports=5432&pool.ports=5433'
# → OPTIONS: {
# 'pool': {
# 'hosts': ['host1', 'host2'],
# 'ports': [5432, 5433],
# },
# }
# Deep nesting and mixed types
'postgres://user:pass@host/db?cluster.nodes.primary=node1&cluster.weights=10&cluster.weights=20&cluster.enabled=true'
# → OPTIONS: {
# 'cluster': {
# 'nodes': {'primary': 'node1'},
# 'weights': [10, 20],
# 'enabled': True,
# },
# }
URL Fragments for Top-Level Configuration
URL fragments (after #) create top-level Django configuration keys, ideal for database settings like CONN_MAX_AGE, AUTOCOMMIT, or test configurations:
# Database with connection settings and testing config
'postgresql://user:pass@host:5432/db?pool=true#CONN_MAX_AGE=42&TEST.DATABASES.NAME=testdb'
# → {
# 'ENGINE': 'django.db.backends.postgresql',
# 'NAME': 'db',
# 'USER': 'user', 'PASSWORD': 'pass', 'HOST': 'host', 'PORT': 5432,
# 'OPTIONS': {'pool': True},
# 'CONN_MAX_AGE': 42,
# 'TEST': {'DATABASES': {'NAME': 'testdb'}},
# }
# Cache with top-level timeout and testing config
'redis://localhost:6379/1?timeout=300#KEY_PREFIX=prod&VERSION=2&TEST.CACHE.BACKEND=dummy'
# → {
# 'BACKEND': 'django.core.cache.backends.redis.RedisCache',
# 'LOCATION': 'redis://localhost:6379/1',
# 'TIMEOUT': 300,
# 'KEY_PREFIX': 'prod', 'VERSION': 2,
# 'TEST': {'CACHE': {'BACKEND': 'dummy'}},
# }
URL Encoding for Credentials, Hostnames, and Paths
Username, password, hostname, and path fields are automatically URL-decoded, allowing you to use special characters without manual encoding in your configuration:
# Special characters in credentials are automatically decoded
'postgres://user%40domain:p%40ss%23word@localhost:5432/mydb'
# → USER: 'user@domain', PASSWORD: 'p@ss#word'
# Complex passwords with spaces and special characters
'postgres://my%2Fuser:pass%20word%21%40%23%24@localhost:5432/db'
# → USER: 'my/user', PASSWORD: 'pass word!@#$'
# Hostnames with special characters (case-sensitive)
'postgres://user:pass@My%2DServer%2EExample%2ECom:5432/db'
# → HOST: 'My-Server.Example.Com' (case preserved)
# Database names/paths with spaces and special characters (case-sensitive)
'postgres://user:pass@host:5432/My%20Database%2DName'
# → NAME: 'My Database-Name'
# SQLite file paths with spaces and special characters
'sqlite:///C%3A/Users/My%20User/AppData/My%20Database%20File.db'
# → NAME: 'C:/Users/My User/AppData/My Database File.db'
# Complex paths with multiple special characters
'postgres://user:pass@host:5432/path%2Fto%2Fdb%40company%23123'
# → NAME: 'path/to/db@company#123'
When to URL-encode:
@symbol:%40(separates credentials from host):symbol:%3A(separates username from password, or port)/symbol:%2F(separates components)#symbol:%23(starts fragment)?symbol:%3F(starts query string).symbol:%2E(in hostnames if you need literal dots in server names)-symbol:%2D(in hostnames if needed)- Space:
%20 - Other special chars:
!→%21,$→%24, etc.
Note: Case sensitivity is preserved for hostnames and paths during URL decoding.
Example with environment variables:
# In your .env file or environment
DATABASE_URL="postgres://admin%40company:P%40ssw0rd%21@db.example.com:5432/production"
# In settings.py
DATABASES = {
'default': os.environ['DATABASE_URL']
}
# → USER: 'admin@company', PASSWORD: 'P@ssw0rd!'
Backends
Currently django-service-urls supports five different services:
DATABASES (django_service_urls.db)
| Service | Backend | URLString |
|---|---|---|
| Postgresql | django.db.backends.postgresql | postgres://user:passwd@host:port/db |
| Postgresql Socket | django.db.backends.postgresql | postgres://%2Fvar%2Frun%2Fpostgresql/db |
| Postgresql (dj-database-url compat alias) | django.db.backends.postgresql | postgresql://user:passwd@host:port/db |
| Postgresql (dj-database-url compat alias) | django.db.backends.postgresql | pgsql://user:passwd@host:port/db |
| Postgis | django.contrib.gis.db.backends.postgis | postgis://user:passwd@host:port/db |
| Sqlite (memory) | django.db.backends.sqlite3 | sqlite://:memory: or sqlite:// |
| Sqlite (file) | django.db.backends.sqlite3 | sqlite:///var/db/database.db |
| Sqlite+ (production settings) | django.db.backends.sqlite3 | sqlite+:///var/db/database.db |
| Spatialite (memory) | django.contrib.gis.db.backends.spatialite | spatialite://:memory: or spatialite:// |
| Spatialite (file) | django.contrib.gis.db.backends.spatialite | spatialite:///var/db/database.db |
| Mysql | django.db.backends.mysql | mysql://user:passwd@host:port/db |
| Mysql + GIS | django.contrib.gis.db.backends.mysql | mysql+gis://user:passwd@host:port/db |
| Mysql GIS (dj-database-url compat alias) | django.contrib.gis.db.backends.mysql | mysqlgis://user:passwd@host:port/db |
| Oracle | django.db.backends.oracle | oracle://user:passwd@host:port/db |
| Oracle + GIS | django.contrib.gis.db.backends.oracle | oracle+gis://user:passwd@host:port/db |
| Oracle GIS (dj-database-url compat alias) | django.contrib.gis.db.backends.oracle | oraclegis://user:passwd@host:port/db |
| MSSQL | sql_server.pyodbc | mssql://user:passwd@host:port/db |
| MSSQL (Microsoft driver) | mssql | mssqlms://user:passwd@host:port/db |
| Redshift | django_redshift_backend | redshift://user:passwd@host:port/db |
| CockroachDB | django_cockroachdb | cockroach://user:passwd@host:port/db |
| Timescale | timescale.db.backends.postgresql | timescale://user:passwd@host:port/db |
| Timescale + GIS | timescale.db.backend.postgis | timescale+gis://user:passwd@host:port/db |
| Timescale GIS (dj-database-url compat alias) | timescale.db.backend.postgis | timescalegis://user:passwd@host:port/db |
SQLite+ for Production
The sqlite+:// protocol provides an optimized SQLite configuration for production use,
based on recommendations from dj-lite.
It automatically includes:
- WAL (Write-Ahead Logging) mode for better concurrency
- IMMEDIATE transaction mode to reduce lock contention
- Memory-mapped I/O for improved performance
- Optimized PRAGMA settings for production workloads
# Simple production-ready configuration
DATABASES = {
'default': 'sqlite+:///path/to/database.db'
}
# Resulting configuration:
# {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': '/path/to/database.db',
# 'OPTIONS': {
# 'transaction_mode': 'IMMEDIATE',
# 'timeout': 5,
# 'init_command': '''PRAGMA journal_mode=WAL;
# PRAGMA synchronous=NORMAL;
# PRAGMA temp_store=MEMORY;
# PRAGMA mmap_size=134217728;
# PRAGMA journal_size_limit=27103364;
# PRAGMA cache_size=2000;'''
# }
# }
You can override any default setting using query parameters:
# Custom timeout and transaction mode
'sqlite+:///db.sqlite3?timeout=10&transaction_mode=DEFERRED'
Overriding PRAGMA settings: Use URL fragments with PRAGMA. prefix to override or add PRAGMA values:
# Override default journal_mode
'sqlite+:///db.sqlite3#PRAGMA.journal_mode=DELETE'
# Override multiple PRAGMA settings
'sqlite+:///db.sqlite3#PRAGMA.journal_mode=DELETE&PRAGMA.synchronous=FULL'
# Add new PRAGMA settings while keeping defaults
'sqlite+:///db.sqlite3#PRAGMA.busy_timeout=5000'
# Combine with other fragment settings
'sqlite+:///db.sqlite3#PRAGMA.journal_mode=WAL&CONN_MAX_AGE=600'
SQLite with Custom PRAGMA Settings
The regular sqlite:// protocol also supports PRAGMA settings via URL fragments:
# Add PRAGMA settings to standard SQLite
DATABASES = {
'default': 'sqlite:///path/to/db.sqlite3#PRAGMA.journal_mode=WAL&PRAGMA.synchronous=NORMAL'
}
# Works with spatialite too
DATABASES = {
'default': 'spatialite:///path/to/spatial.db#PRAGMA.journal_mode=WAL'
}
This allows you to customize SQLite behavior without using the production defaults from sqlite+://.
CACHES (django_service_urls.cache)
| Service | Backend | URLString |
|---|---|---|
| Memory | django.core.cache.backends.locmem.LocMemCache | memory:// |
| Memory | django.core.cache.backends.locmem.LocMemCache | memory://abc |
| Database | django.core.cache.backends.db.DatabaseCache | db://table-name |
| Dummy | django.core.cache.backends.dummy.DummyCache | dummy:// |
| Dummy | django.core.cache.backends.dummy.DummyCache | dummy://abc |
| PyMemcached: single ip | django.core.cache.backends.memcached.PyMemcachedCache | pymemcached://1.2.3.4:1567 |
| PyLibMCCache: single ip | django.core.cache.backends.memcached.PyLibMCCache | pylibmccache://1.2.3.4:1567 |
| Memcached: single ip | django.core.cache.backends.memcached.MemcachedCache | memcached://1.2.3.4:1567 |
| PyMemcached multiple ips | django.core.cache.backends.memcached.PyMemcachedCache | pymemcached://1.2.3.4:1567,1.2.3.5:1568 |
| PyLibMCCache multiple ips | django.core.cache.backends.memcached.PyLibMCCache | pylibmccache://1.2.3.4:1567,1.2.3.5:1568 |
| Memcached multiple ips | django.core.cache.backends.memcached.MemcachedCache | memcached://1.2.3.4:1567,1.2.3.5:1568 |
| PyMemcached no port | django.core.cache.backends.memcached.PyMemcachedCache | pymemcached://1.2.3.4 |
| PyLibMCCache no port | django.core.cache.backends.memcached.PyLibMCCache | pylibmccache://1.2.3.4 |
| Memcached no port | django.core.cache.backends.memcached.MemcachedCache | memcached://1.2.3.4 |
| PyMemcached unix socket | django.core.cache.backends.memcached.PyMemcachedCache | pymemcached:///tmp/memcached.sock |
| PyLibMCCache unix socket | django.core.cache.backends.memcached.PyLibMCCache | pylibmccache:///tmp/memcached.sock |
| Memcached unix socket | django.core.cache.backends.memcached.MemcachedCache | memcached:///tmp/memcached.sock |
| File | django.core.cache.backends.filebased.FileBasedCache | file://C:/abc/def/xyz |
| File | django.core.cache.backends.filebased.FileBasedCache | file:///abc/def/xyz |
EMAIL (django_service_urls.email)
| Service | Backend | URLString |
|---|---|---|
| Console | django.core.mail.backends.console.EmailBackend | console:// |
| SMTP | django.core.mail.backends.smtp.EmailBackend | smtp://localhost:25 |
| SMTPS (smtp+tls alias) | django.core.mail.backends.smtp.EmailBackend | smtps://localhost:465 |
| SMTP+TLS | django.core.mail.backends.smtp.EmailBackend | smtp+tls://localhost:465 |
| SMTP+SSL | django.core.mail.backends.smtp.EmailBackend | smtp+ssl://localhost:587 |
| File | django.core.mail.backends.filebased.EmailBackend | file:///var/log/emails |
| Memory | django.core.mail.backends.locmem.EmailBackend | memory:// |
| Dummy | django.core.mail.backends.dummy.EmailBackend | dummy:// |
STORAGES (django_service_urls.storage)
| Service | Backend | URLString |
|---|---|---|
| Custom backend | (specified in URL) | storage://your.storage.Backend |
| FileSystem | django.core.files.storage.filesystem.FileSystemStorage | fs:// |
| InMemory | django.core.files.storage.memory.InMemoryStorage | memory:// |
| StaticFiles | django.contrib.staticfiles.storage.StaticFilesStorage | static:// |
| ManifestStaticFiles | django.contrib.staticfiles.storage.ManifestStaticFilesStorage | manifest:// |
| WhiteNoise | whitenoise.storage.CompressedStaticFilesStorage | whitenoise:// |
| WhiteNoise + Manifest | whitenoise.storage.CompressedManifestStaticFilesStorage | whitenoise+static:// |
| S3 | storages.backends.s3.S3Storage | s3:// |
| S3 Static | storages.backends.s3.S3StaticStorage | s3+static:// |
| S3 Manifest | storages.backends.s3.S3ManifestStaticStorage | s3+manifest:// |
| LibCloud | storages.backends.apache_libcloud.LibCloudStorage | libcloud:// |
| Azure | storages.backends.azure_storage.AzureStorage | azure:// |
| Dropbox | storages.backends.dropbox.DropboxStorage | dropbox:// |
| FTP | storages.backends.ftp.FTPStorage | ftp:// |
| Google Cloud | storages.backends.gcloud.GoogleCloudStorage | google:// |
| SFTP | storages.backends.sftpstorage.SFTPStorage | sftp:// |
TASKS (django_service_urls.task)
| Service | Backend | URLString |
|---|---|---|
| Custom backend | (specified in URL) | task://your.task.Backend |
| Dummy | django.tasks.backends.dummy.DummyBackend | dummy:// |
| Immediate | django.tasks.backends.immediate.ImmediateBackend | immediate:// |
| Dummy (django-tasks) | django_tasks.backends.dummy.DummyBackend | dummy+dt:// |
| Immediate (django-tasks) | django_tasks.backends.immediate.ImmediateBackend | immediate+dt:// |
| Database (django-tasks) | django_tasks.backends.database.DatabaseBackend | database+dt:// |
| RQ (django-tasks) | django_tasks.backends.rq.RQBackend | rq+dt:// |
Installation
Install package
$ python3 -m pip install django-service-urls
add import django_service_urls.loads in your manage.py
#!/usr/bin/env python
import os
import sys
import django_service_urls.loads
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project_name.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()
and in wsgi.py
import os
import django_service_urls.loads
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project_name.settings')
application = get_wsgi_application()
Handling Mixed URL and Backend Strings
In some cases, you may want to support both URL strings and traditional Django backend paths (e.g., for backward compatibility). You can use a try-except pattern with ValidationError:
from django_service_urls import email, ValidationError
# Try to parse as URL; if it fails, treat it as a backend path
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
try:
email_config = email.parse(EMAIL_BACKEND)
except ValidationError:
# Not a URL, use as-is (it's a backend path string)
pass
else:
# Successfully parsed as URL, extract configuration
EMAIL_BACKEND = email_config.get('ENGINE')
EMAIL_HOST = email_config.get('HOST')
EMAIL_PORT = email_config.get('PORT')
# ... etc
This pattern is especially useful when migrating from traditional Django settings to URL-based configuration, allowing you to support both formats during the transition.
Extend django-service-urls
Add another handler
You can add another handler to an already existing handler:
my_postgres_backend/service_url.py
from django_service_urls.services import db, postgresql_config_from_url
# postgresql fork
postgresql_config_from_url = db.register(('mypgbackend', 'my_postgres_backend'))(postgresql_config_from_url)
yourapp/settings.py
import my_postgres_backend.service_url
DATABASES = {'default': 'mypgbackend://user:pwd@:/mydb'}
Add another service
from django_service_urls import Service
class SearchService(Service):
def config_from_url(self, engine, scheme, url):
parsed = self.parse_url(url)
return {
'ENGINE': engine,
# here all options from parsed
}
search = SearchService()
@search.register(('myengine', 'my_search_engine'))
def search_config_from_url(backend, engine, scheme, url):
return backend.config_from_url(engine, scheme, url)
mypy integration
If you need to load the initializer from mypy you could add
[mypy]
plugins = django_service_urls.mypy
in your mypy.ini or setup.cfg file.
pyproject.toml configuration is also supported:
[tool.mypy]
plugins = ["django_service_urls.mypy"]
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_service_urls-2.1.tar.gz.
File metadata
- Download URL: django_service_urls-2.1.tar.gz
- Upload date:
- Size: 41.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e5251665840c1e10f37f1a365040e77a44f2ce37e6a6e1d66e0f198ff2ff47db
|
|
| MD5 |
4a93b87d5fd7b7286729bc95b24dd046
|
|
| BLAKE2b-256 |
93ab12ab31dff67c47aa3097c7452c6b6f92602db7bd26c897baa4ac56aeec6e
|
File details
Details for the file django_service_urls-2.1-py3-none-any.whl.
File metadata
- Download URL: django_service_urls-2.1-py3-none-any.whl
- Upload date:
- Size: 31.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e9f5e4e9a9a40e13633a30e73a513a1b472167db6ec7affa976ac475b4e78782
|
|
| MD5 |
59d1ae501453c406a86a07c9851ccf19
|
|
| BLAKE2b-256 |
8247eaf5fa0ebbd80beaaefc221dc2d39b69c1efdea9eebb4a9740996291ad96
|