django-service-urls 2.0 Release Announcement
We’re excited to announce the release of **django-service-urls 2.0**! This major release brings significant improvements, new features, and enhanced type safety while maintaining the simplicity and elegance you’ve come to expect.
django-service-urls is a Django configuration helper that allows you to represent complex service settings (databases, caches, email, storages, and task backends) as simple, portable URL strings. Instead of managing multiple configuration variables across different environments, you can use a single URL like `postgres://user:pass@host:5432/db` or set it via environment variables. This approach simplifies deployment, makes configuration more maintainable, and follows the principles of 12-factor apps.
What’s New
Python and Django Support
- Python: 3.10, 3.11, 3.12, 3.13, 3.14
- Django: 3.2, 4.0, 4.1, 4.2, 5.0, 5.1, 5.2, 6.0
Advanced Configuration with Query Strings and Fragments
Since version 1.9, django-service-urls has supported advanced configuration through nested dictionaries, lists, and URL fragments. This means you can express complex settings in a single URL string:
"postgres://user:pass@host:5432/db?pool.min_size=2&pool.max_size=10#CONN_MAX_AGE=300"
# → {
# "ENGINE": "django.db.backends.postgresql",
# "NAME": "db",
# "USER": "user",
# "PASSWORD’: "pass",
# "HOST": "host",
# "PORT": 5432,
# "OPTIONS": {
# "pool": {"min_size": 2, "max_size": 10}
# },
# "CONN_MAX_AGE": 300
# }
Query parameters with dot notation create nested configuration options, while URL fragments define top-level Django settings.
Support for Storage and Task Backends
Version 2.0 adds support for two new Django service types:
Storage Backends - Configure Django’s STORAGES setting via URLs:
STORAGES = {
"default": "s3://?bucket_name=mybucket®ion_name=us-east-1",
"staticfiles": "whitenoise://",
}
Task Backends - Configure django-tasks via URLs:
TASKS = {
"default’: ‘database+dt://",
"background’: ‘rq+dt://localhost:6379/0",
}
Automatic URL Decoding
All URL components are now automatically URL-decoded while preserving case sensitivity:
# Credentials with special characters
"postgres://user%40company:P%40ssw0rd%21@host:5432/db"
# → USER: ‘user@company’, PASSWORD: ‘P@ssw0rd!’
# Database paths with spaces
"sqlite:///My%20Database/Test%20File.db"
# → NAME: ‘/My Database/Test File.db’
# Hostnames with special characters
"postgres://user:pass@My%2DServer.Example.Com:5432/db"
# → HOST: ‘My-Server.Example.Com’ (case preserved!)
Full Type Safety
The entire codebase now includes comprehensive mypy type annotations.
Enhanced URL Parsing
New UrlInfo dataclass - Replaces the previous dictionary-based approach:
from django_service_urls import UrlInfo
from django_service_urls.parse import parse_url
url_info: UrlInfo = parse_url(“postgres://user:pass@host:5432/db”)
print(url_info.hostname) # Type-safe attribute access
print(url_info.port) # Integer, not string!
Expanded Database Backend Support
A new sqlite+:// protocol provides production-optimized SQLite configuration with WAL mode, IMMEDIATE transactions, memory-mapped I/O, and other performance optimizations based on dj-lite recommendations.
Both sqlite:// and sqlite+:// protocols now support PRAGMA configuration via URL fragments, allowing you to customize SQLite behavior inline:
# Override production PRAGMA settings
DATABASES = {
"default": "sqlite+:///db.sqlite3#PRAGMA.journal_mode=DELETE&PRAGMA.busy_timeout=5000"
}
# Add PRAGMA settings to standard SQLite
DATABASES = {
"default": "sqlite:///db.sqlite3#PRAGMA.journal_mode=WAL&PRAGMA.synchronous=NORMAL"
}
Additionally, new database backends aligned with `dj-database-url`: MSSQL, Redshift, CockroachDB, Timescale (with GIS support), and GIS variants for MySQL and Oracle.
Breaking Changes
1. Removed service_urls Shim Module
The backward-compatibility shim has been removed. Update your imports:
Before:
from service_urls import db, cache, email
After:
from django_service_urls import db, cache, email
2. Removed Service.validate() Method
The validate() method has been removed in favor of a more Pythonic EAFP (Easier to Ask for Forgiveness than Permission) pattern:
Before:
if email.validate(settings.EMAIL_BACKEND):
email_config = email.parse(settings.EMAIL_BACKEND)
After:
from django_service_urls import ValidationError
try:
email_config = email.parse(settings.EMAIL_BACKEND)
except ValidationError:
# It’s a backend path string, not a URL
…
else:
…
3. Changed Parse Result Structure
parse_url() now returns a UrlInfo dataclass instead of a dictionary:
Before:
parsed = parse_url(url)
hostname = parsed[“hostname”] # Dictionary access
After:
parsed = parse_url(url)
hostname = parsed.hostname # Attribute access
4. Automatic URL Decoding
URL components are now automatically decoded. If you were manually decoding URLs, remove that code:
Before:
from urllib.parse import unquote
username = unquote(parsed_url.username)
After:
username = parsed_url.username # Already decoded!
##
Resources
- Documentation: README.md
- Changelog: CHANGELOG.md
- Issues: Issues Github
- PyPI: PyPI
Getting Started
Install or upgrade:
uv add django-service-urls
Or with `pip`:
pip install --upgrade django-service-urls
Feedback
We’d love to hear your feedback! Please:
- Report issues on GitHub
- Share your use cases and suggestions
- Contribute improvements via pull requests
-–
Happy configuring! 
The django-service-urls team