---
sidebar_position: 9
---

# 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.

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

## Your server

```python
# 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(),
)
```

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

```python
def order(request, pk):
    return render(request, "order.html", {"realtime": wr.config_for(realtime, request), "order": …})
```

```html
{{ 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

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

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

```python
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())
```
