Django channels + HTMX -> notification messages framework

I'd like to implement a notification framework for Django that uses channels/websockets and HTMX. But I don't get how to do that. Some things work, some are extremely complicated n my mind at least. Here's the code:

from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
from channels.layers import get_channel_layer
from django.contrib import messages
from django.template.loader import get_template
from medux.common.models import User

class NotificationConsumer(WebsocketConsumer):
    def connect(self):
        self.user = self.scope["user"]
        if not self.user.is_authenticated:
            self.close()
            return

        # create a group "user-<username>"
        async_to_sync(self.channel_layer.group_add)(
            f"user-{self.user.username}",
            self.channel_name,
        )
        self.accept()

    def disconnect(self, close_code):
        async_to_sync(self.channel_layer.group_discard)(
            f"user-{self.user.username}",
            self.channel_name,
        )

    def notify(self, event: dict):
        html = get_template("common/toast.html").render(
            context={"message": event["message"]}
        )
        self.send(text_data=html)
<div hx-ext="ws" ws-connect="/ws/messages">
  <div id="toasts" class="toast-container position-fixed bottom-0 end-0 p-3 pb-5">
    {% for message in messages %}
      <div
          id="toasts"
          hx-swap-oob="beforeend">
        <div
            data-toast-template
            class="toast align-items-center border-0 show"
            role="alert"
            aria-live="assertive"
            aria-atomic="true">

          {# header here #}

          <div data-toast-body class="toast-body">{{ message }}</div>
          <button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
        </div>
      </div>
    {% endfor %}
  </div>
</div>

The client connects to the consumer, and if I write a small `self.send(text_data="foo")

AFAIK, I have to send an "high level" event to the consumer with the type "notify", so the notify() method is called. So this would be a code called anywhere in a view e.g.:

from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
channel_layer = get_channel_layer()

async_to_sync(channel_layer.group_send)(
    "user-christian",
    {"type": "notify", "message": "<div id='toasts' hx-swap-oob='beforeend'>blah</div>"}
)

I don't step through the internals of channels, no matter how often I read the docs.. The docs how to use group_add and group_send are sparse, and I am sure I am using them wrong.

Could anyone give me some hints here? In the next step, I would replace Django's messaging framework with this solution in my application.

I got that things to work with Turbo Django, which is quite convenient to work with, but lacks triggers like HTMX that let me iniciate AJAX calls like hx-post or hx-get.

Back to Top