Skip to main content
Markdown

Django

wordplus_realtime.django gives Django the browser's routes for the session's user, the page's settings with the CSRF token, and a user's profile from Django's user model.

pip install 'wordplus-realtime[django]'
npm install @wordplus/realtime

Your server​

# shop/realtime.py
from wordplus_realtime import Realtime
import wordplus_realtime.django as wr

realtime = Realtime.from_environment(
profile=wr.user_profile, # the user's full name, or their username
worker="/static/realtime-worker.js",
rooms="/static/realtime-rooms.min.js",
)

realtime.channel(
"private-orders-",
lambda user, channel: Order.objects.filter(pk=channel[15:], user_id=user).exists(),
)
# urls.py: POST /api/realtime/auth, /calls and /keys
from shop.realtime import realtime

urlpatterns = [
path("api/realtime/", include(wr.urls(realtime))),
]

The routes take the session's user (request.user), and Django's CSRF check stays on. For another way to know the user, pass it in: wr.urls(realtime, user=lambda request: …).

npx wordplus-realtime copy static/ puts the two files in a folder of your STATICFILES_DIRS, and collectstatic takes them from there.

The page​

def order(request, pk):
return render(request, "order.html", {"realtime": wr.config_for(realtime, request), "order": …})
{{ realtime|json_script:"realtime" }}
<script type="module">
import { connect } from '@wordplus/realtime'; // through your bundler

const rt = await connect( JSON.parse( document.getElementById( 'realtime' ).textContent ) );
rt.subscribe( 'private-orders-{{ order.pk }}' ).on( 'updated', ( data ) => refresh( data ) );
</script>

config_for() gives the page its settings for the request's user: the routes with the CSRF token as X-CSRFToken, and the user's keys, so the page connects with no request first.

Publishing​

realtime.publish(f"private-orders-{order.pk}", "updated", {"status": order.status})

Publish after your own work is saved, transaction.on_commit() for one. A failed publish raises RealtimeError, and its result is a missed live update, not a lost change. In an async view, use await realtime.apublish(…).

Profiles​

wr.user_profile shows a user's full name, or their username when they have none. For more, such as an avatar, write your own:

def profile(user_id):
user = User.objects.filter(pk=user_id).select_related("profile").first()
return {"name": user.get_full_name(), "avatar": user.profile.avatar_url} if user else None

realtime = Realtime.from_environment(profile=profile)

Rotating keys​

realtime.rotate_channel(name) and realtime.rotate_user(user_id) need somewhere every server sees to keep the keys' generations. A cache that doesn't evict works, such as Redis or the database cache, but not the local memory one:

from django.core.cache import caches

class CacheGenerations:
def get(self, name):
return caches["default"].get("wordplus-realtime:" + name)

def set(self, name, generation):
caches["default"].set("wordplus-realtime:" + name, generation, timeout=None)

realtime = Realtime.from_environment(profile=wr.user_profile, generations=CacheGenerations())