Metadata-Version: 2.4
Name: django-rsgi
Version: 1.0.0
Summary: RSGI adapter for Django applications
Project-URL: Homepage, https://github.com/myers/django-rsgi
Project-URL: Repository, https://github.com/myers/django-rsgi
Project-URL: Issues, https://github.com/myers/django-rsgi/issues
Author: Django RSGI Contributors
License: MIT
License-File: LICENSE
Keywords: async,django,granian,rsgi,web
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 6.0
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.12
Requires-Dist: asgiref>=3.9.1
Requires-Dist: django>=6.0
Provides-Extra: server
Requires-Dist: granian[reload]>=2.6.0; extra == 'server'
Description-Content-Type: text/markdown

# 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](https://github.com/emmett-framework/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](https://github.com/emmett-framework/granian/blob/master/docs/spec/RSGI.md) (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 `FileResponse` objects are automatically served using Granian's `response_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_required` or 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

```bash
pip install django-rsgi[server]
```

Or using `uv`:

```bash
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:

```bash
pip install django-rsgi
```

## Quick Start

### 1. Add to INSTALLED_APPS

```python
INSTALLED_APPS = [
    ...
    'django_rsgi',
]
```

### 2. Generate your RSGI entry point

```bash
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

```bash
python manage.py serve
```

Or run Granian directly:

```bash
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:

```bash
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](https://docs.djangoproject.com/en/6.0/topics/async/) 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:

```python
# 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:

```bash
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:

```bash
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:

```python
# 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:

```python
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:

```python
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.

```bash
# 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

```bash
bin/test
```

Arguments are passed through to pytest:

```bash
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.
