RSGI adapter for Django applications
Project description
django-rsgi
Deploy Django with media serving in a single process — no nginx, no gunicorn.
django-rsgi lets you serve your Django app and user-uploaded media files from a single Granian process. No reverse proxy for static/media files, no separate WSGI server — just one process that handles everything.
It does this by adapting Django to speak RSGI (Rust Server Gateway Interface), Granian's native protocol. Your Django code stays unchanged — django-rsgi handles the translation.
Features
- Single-process deployment — Granian serves HTTP, Django handles logic, and media files are served efficiently with zero-copy and range requests
- FileResponse optimization — Django's
FileResponseobjects are automatically served using Granian'sresponse_file()for zero-copy file delivery - HTTP Range requests — Partial content delivery for video streaming and resumable downloads, out of the box
- Authenticated file serving — Serve user uploads behind
login_requiredor custom permissions with path traversal protection - Async-native — Full support for Django's async views and streaming responses
- Django 6.0+ — Built for modern Django with Python 3.12+
Installation
pip install django-rsgi[server]
Or using uv:
uv pip install django-rsgi[server]
The [server] extra installs Granian. If you only need the RSGI handler (e.g., for testing or use with another RSGI server), you can install without it:
pip install django-rsgi
Quick Start
1. Add to INSTALLED_APPS
INSTALLED_APPS = [
...
'django_rsgi',
]
2. Generate your RSGI entry point
python manage.py rsgi_install myproject
This creates myproject/rsgi.py configured for your project — similar to the wsgi.py and asgi.py that Django generates.
3. Run with Granian
python manage.py serve
Or run Granian directly:
granian --interface rsgi myproject.rsgi:application
Try the demo
The example_project/ directory is a working Django app with video streaming, SSE, and media serving:
cd example_project
./bin/setup # migrations, fixtures, admin user
uv run ./manage.py serve
# Visit http://localhost:8000
Advanced Usage
Async Views and Streaming Responses
django-rsgi supports Django's async views for efficient streaming responses and Server-Sent Events. See the Django async documentation for detailed information on async views.
The included example project demonstrates a simple SSE endpoint at /sse-time/ that streams the current time every second.
Static File Serving
django-rsgi automatically optimizes Django's FileResponse objects for efficient file serving. This includes:
- Zero-copy file serving: Files are served directly from disk using Granian's
response_file()method - HTTP Range requests: Automatic support for partial content delivery (useful for video streaming and resumable downloads)
- Efficient streaming: Large files are streamed in chunks without loading into memory
Development Mode
In development (DEBUG=True), use Django's built-in static file serving:
# urls.py
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
urlpatterns = [
# your patterns...
]
if settings.DEBUG:
urlpatterns += staticfiles_urlpatterns()
Production Mode
In production, configure Granian to serve static files directly:
python manage.py collectstatic # Collect static files first
granian --interface rsgi myproject.rsgi:application \
--static-path-route /static \
--static-path-mount /path/to/staticfiles
Or use the management command which handles this automatically:
python manage.py serve # Automatically configures static file serving based on DEBUG setting
Reload Settings
In development mode, manage.py serve enables auto-reload. You can exclude directories and paths from the file watcher via Django settings:
# settings.py
GRANIAN_RELOAD_IGNORE_DIRS = ["node_modules", ".git"]
GRANIAN_RELOAD_IGNORE_PATHS = ["data/cache.db"]
Authenticated File Serving
For files that require authentication or permission checks (like user uploads), django-rsgi provides secure file serving:
from django_rsgi.urls import media_urlpatterns
urlpatterns = [
# your patterns...
]
if settings.DEBUG:
# Serve MEDIA_ROOT with login_required by default
urlpatterns += media_urlpatterns()
For custom permissions:
from django.contrib.auth.decorators import permission_required
from django_rsgi.serve import serve_file
from django_rsgi.urls import media_urlpatterns
# Use a custom permission check
urlpatterns += media_urlpatterns(
view=permission_required('can_view_files')(serve_file)
)
# Or serve from a different root
urlpatterns += media_urlpatterns(
prefix='/private/',
document_root='/path/to/private/files'
)
Security Features:
- Path traversal protection using Python's
pathlib - No directory listing
- Automatic MIME type detection
- ETag support for efficient caching
- If-Modified-Since and If-None-Match handling
- All FileResponse optimizations (range requests, zero-copy serving)
Note: If your files don't need authentication, use Django's built-in static file serving with django.conf.urls.static.static() which is also optimized by django-rsgi.
FAQ
What about WebSockets?
Django itself has no built-in WebSocket support — even with ASGI, you need Django Channels for that. django-rsgi follows the same approach: it handles HTTP only. Use Django Channels or a dedicated WebSocket server alongside Granian if you need WebSockets.
Development
Setting up the development environment
This project uses uv as the package manager for faster and more reliable dependency management.
# Clone the repository
git clone https://github.com/myers/django-rsgi
cd django-rsgi
# Create a virtual environment and install dev dependencies
uv sync
# Install commit hooks
prek install
Running tests
bin/test
Arguments are passed through to pytest:
bin/test -xvs # verbose with stop on first failure
bin/test tests/test_rsgi_handler.py # specific file
bin/test --cov=django_rsgi # with coverage
License
MIT License
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
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_rsgi-1.0.0.tar.gz.
File metadata
- Download URL: django_rsgi-1.0.0.tar.gz
- Upload date:
- Size: 10.6 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bf644403c6dd5376e0b1d1785cf9e39a99a09c3b2fe9997c26897b11418768df
|
|
| MD5 |
35c814ae56d00fd350a9406f54c3cf9c
|
|
| BLAKE2b-256 |
052e19fd237f2daee03340b472b234ff5dfbcfbe466df5929538559c47f6392a
|
Provenance
The following attestation bundles were made for django_rsgi-1.0.0.tar.gz:
Publisher:
publish.yml on myers/django-rsgi
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_rsgi-1.0.0.tar.gz -
Subject digest:
bf644403c6dd5376e0b1d1785cf9e39a99a09c3b2fe9997c26897b11418768df - Sigstore transparency entry: 984644428
- Sigstore integration time:
-
Permalink:
myers/django-rsgi@f680a81e25ebbc39621ca64a3ac3a6a02a1bacbc -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/myers
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f680a81e25ebbc39621ca64a3ac3a6a02a1bacbc -
Trigger Event:
push
-
Statement type:
File details
Details for the file django_rsgi-1.0.0-py3-none-any.whl.
File metadata
- Download URL: django_rsgi-1.0.0-py3-none-any.whl
- Upload date:
- Size: 25.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c6f0f2a69c521a46f478b17cadca49440469aea9832a06cf95aff5d1e6f536b6
|
|
| MD5 |
5cbc86542853fe19feb24e4b83cccd08
|
|
| BLAKE2b-256 |
e40161abd33def055d624ea216739a5d645c586a1a12d9ca9c64a9d21b78973a
|
Provenance
The following attestation bundles were made for django_rsgi-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on myers/django-rsgi
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_rsgi-1.0.0-py3-none-any.whl -
Subject digest:
c6f0f2a69c521a46f478b17cadca49440469aea9832a06cf95aff5d1e6f536b6 - Sigstore transparency entry: 984644434
- Sigstore integration time:
-
Permalink:
myers/django-rsgi@f680a81e25ebbc39621ca64a3ac3a6a02a1bacbc -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/myers
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f680a81e25ebbc39621ca64a3ac3a6a02a1bacbc -
Trigger Event:
push
-
Statement type: