DjangoRealtime
Realtime browser events, SSE for Django + PostgreSQL
Install / Use
/learn @usmanhalalit/DjangoRealtimeREADME
DjangoRealtime
Add realtime capabilities to your Django web app in no time. No WebSockets, no Channels, no processes to run, no Redis. Just set up and go!
Django + PostgreSQL = Realtime Updates in Browser
Live Demo
Check out the live demo at and chat in 90s-style chatroom:
Built with HTMX and 6 lines of vanilla JavaScript! Code is in examples/chatroom.
Basic Usage
from djangorealtime import publish
publish(user_id=user.id, event_type='task_complete', detail={'task_id': 123, 'status': 'success'})
Browsers receive it instantly ✨:
window.addEventListener('djr:task_complete', (e) => {
console.log(e.detail); // {task_id: 123, status: 'success'}
});
How it works
Built on HTTP Server-Sent Events (SSE) and native PostgreSQL NOTIFY/LISTEN. Everything is auto-configured:
- Secure by default - events are user-scoped by default
- Works everywhere - SSE is your standard HTTP, no WebSocket complexity
- Scales across workers - multiple Django processes can communicate via PostgreSQL
- Zero fluff - runs on your existing Django + PostgreSQL stack.
- Automatic reconnection - handles network interruptions seamlessly, for both client and server
- Event persistence - events stored in database for reliability and replay
- Django admin integration - view and replay events from the admin panel
- Sync or async views - keep using sync views, only make sure to use asgi server
Use Cases
- Update UI state without polling
- Notify users of background task completion
- Realtime messaging and chat
- Communicate between multiple Django instances or background workers
- Event-driven applications
- Live dashboards and notifications, like polls, comments, displays etc
- Flexible event log
- And more!
Table of Contents
Installation
pip install djrealtime
Add to Django:
INSTALLED_APPS = [
# ...
'djangorealtime',
]
Include URLs for automatic endpoint setup:
urlpatterns = [
path('realtime/', include('djangorealtime.urls')),
# ...
]
Database Migration
To create necessary tables, run:
python manage.py migrate djangorealtime
You don't need this step if you disable event storage in Settings.
Please note, you need to have 'django.contrib.postgres' in your INSTALLED_APPS. This is for better indexing.
Frontend Setup
Add this in your base HTML template in <head>.
This will add the necessary JavaScript to automatically connect and listen for events:
{% load djangorealtime_tags %}
{% djangorealtime_js %}
Usage
Publishing Events
User-Scoped Events
from djangorealtime import publish
publish(user_id=user.id, event_type='task_complete', detail={'task_id': 123, 'status': 'success'})
These events are only sent to the specified user who is logged in using Django's authentication system.
user_id is the primary key of your user model. It can be string or integer.
Global Events
from djangorealtime import publish_global
publish_global(event_type='new_update', detail={'data': 'some'})
These events are broadcast to all connected clients or browsers, regardless of authentication.
System Events
from djangorealtime import publish_system
publish_system(event_type='new_pull_request', detail={'pr_id': 456})
These events are sent to internal system processes only, not to browsers. Like another Django instance or a Django management command listening for events.
This also takes optional user_id argument, but only for your reference. Event is still not sent to browsers.
Listening to Events
In your JavaScript code, listen to events using DOM events. Just listen on window
using the djr: prefix before your event type.
window.addEventListener('djr:task_complete', (e) => {
console.log(e.detail); // {task_id: 123, status: 'success'}
});
Advanced Features
Filtering events for entity
You can filter events by entity using a special :id field in the detail dictionary.
This allows you to listen to both general and specific events. Nifty!
publish(user_id=user.id, event_type='page_imported', detail={':id': 42}) # say, page with ID 42
// Listen to specific page_imported event for page ID 42
window.addEventListener('djr:page_imported:42', (e) => {
console.log('Page 42 was imported', e.detail);
});
// djr:page_imported will also be fired
Listening from Backend
You can also listen to events from other backend processes, like Django management commands. You can
subscribe to all events using the subscribe decorator.
from djangorealtime import subscribe, Event
@subscribe
def on_event(event: Event):
print(f"Received {event.scope} event: {event.type} with detail: {event.detail}")
Event Storage
PostgreSQL NOTIFY is not persistent. But we built on top of it to provide reliable event storage out of the box.
All events are efficiently stored in your Django database by default.
There is a limit of 8kB payload per event due to PostgreSQL NOTIFY limitations. We do not think you should even
be passing a fraction of that in normal usage. Use references, IDs, or private_data to keep it light.
Events including detail, activities and private_data are stored in the database, so make sure not to pass sensitive information directly.
Set 'ENABLE_EVENT_STORAGE': False in settings to disable event storage if you don't need it.
Private Data
You can also pass private data with the event that is not sent to clients, but stored in the database for your reference.
Use private_data={} kwarg in publish* functions. This how private data can be retrieved:
event.model().private_data
Django Admin
DjangoRealtime seamlessly integrates with Django admin to provide a simple interface to view events and activities. You can filter events by type, scope etc. And wait, there's more! You can even replay events directly from the admin interface.
Replaying Events
Event model of DjangoRealtime has a replay() method to resend the event.
from djangorealtime.models import Event
event = Event.objects.get(id=1)
event.replay() # Resends the event
Or you can replay from Django admin by selecting events and choosing "Replay selected events" action.
Hooks
You can define custom callback functions to be executed on certain events.
ON_RECEIVE_HOOK
Called when an event is received by the listener, before any processing. Returning None aborts further processing.
from djangorealtime import Event
from datetime import datetime
def on_receive_hook(event: Event) -> Event | None:
print(f"[ON_RECEIVE_HOOK] Event received: {event.type}")
event.detail['received_at'] = datetime.now().isoformat()
return event
BEFORE_SEND_HOOK
Called before sending an event to each client. You can modify or abort the event here.
from djangorealtime import Event
from django.http import HttpRequest
def before_send_hook(event: Event, request: HttpRequest) -> Event | None:
user_info = "anonymous"
if hasattr(request, 'user') and request.user.is_authenticated:
user_info = request.user.email
print(f"[BEFORE_SEND_HOOK] Sending {event.type} to {user_info}")
return event
Hooks can be set in DJANGOREALTIME settings.
Configuration
Performance and Scalability
DjangoRealtime is designed to be lightweight and efficient. We have production apps using DjangoRealtime at scale.
You can use multiple Django instances behind a load balancer. Each instance will have its own listener process. All Django instances communicate via your PostgreSQL instance.
Please note, a listener will always maintain a single persistent database connection to PostgreSQL. If this connection breaks, say when PostgreSQL cluster restarts, the listener will keep logging errors and retrying the connection with exponential backoff.
Other database connections are optimised for low database connection count, so they get closed after operations.
We've seen very low latency with all features enabled. If you want even lower latency, you can disable event storage by
having 'ENABLE_EVENT_STORAGE': False in settings.
All events use a single PostgreSQL channel. Then we demultiplex events in the listener process based on event_type.
Settings
All settings are optional. Add to your Django settings.py if you want to override defaults.
DJANGOREALTIME = {
'AUTO_LISTEN': True, # Auto-start a non-blocking listener thread with web server (default: True)
'EVENT_MODEL': 'djangorealtime.models.Event', # If you want to use a custom event model
'ENABLE_EVENT_STORAGE': True, # Enable/disable event storage in DB (default: True)
'ON_RECEIVE_HOOK': callback_function, # Custom callback on receiving an event
'BEFORE_SEND_HOOK': callback_function, # Custom callback before sending an event to clients
'CONCURRENT_SSE_WORKERS': 1, # Thread pool size for SSE event processing (default:
Related Skills
feishu-drive
332.9k|
things-mac
332.9kManage Things 3 via the `things` CLI on macOS (add/update projects+todos via URL scheme; read/search/list from the local Things database)
clawhub
332.9kUse the ClawHub CLI to search, install, update, and publish agent skills from clawhub.com
SchoolAnalytics
Skill: IB MYP Analytics & Grading Activation Trigger - Any task involving grade calculations, student flagging, or analytics dashboarding. - Questions about Criteria A, B, C, or D. Knowledge
