Skip to content

Compatibility with htmx #134

@aranvir

Description

@aranvir

Hi, I've been trying to use django-eventstream with the htmx sse extension, but can't get it to work quite. I did look into the examples and managed to get those running so the principle environment setup should be fine. I am a beginner for both packages and also django in general so I might miss the obvious.

When I start the app and open the page I can see it connecting to the /events endpoint successfully. However, in the browser console I keep getting error message. It periodically sends another get-request to /events and while the returncode is 200 it seems like htmx does not like the response. If I manually go to /events I get::

event: stream-error
id: error
data: {"condition": "bad-request", "text": "Invalid request: No channels specified."}

No I don't know if htmx faces the same issue or if I only get this because my manual request does not specify a channel. Yet I don't even know how I would do that. I understood from the documentation that it is encouraged to use channels and it is somewhat clear how they are defined and how I can send messages to a channel but I don't understand how the client part selects the channel.

Maybe it's a htmx limitation? But even if I don't use channels, I don't get a proper reply. Would appreciate any help or tips. Or maybe I need to bring this question to the htmx github.

I appended the relevant code below:

mydemo/asgi.py

"""
ASGI config for mydemo project.

It exposes the ASGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/
"""

import os
import django
from django.core.asgi import get_asgi_application
from django.urls import path, re_path
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
import django_eventstream

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "server.settings")

application = ProtocolTypeRouter({
    'http': URLRouter([
        path('events/', AuthMiddlewareStack(
            URLRouter(django_eventstream.routing.urlpatterns)
        ), {'channels': ['test']}),
        re_path(r'', get_asgi_application()),
    ]),
})

mydemo/settings.py

...
INSTALLED_APPS = [
    'daphne',
    'channels',
    'django_eventstream',
    'server_events',
    ...
]
...
MIDDLEWARE = [
    'django_grip.GripMiddleware',
    ...
]
...
ASGI_APPLICATION = "mydemo.asgi.application"

mydemo/urls.py

from django.contrib import admin
from django.urls import path, include
from server_events import views

urlpatterns = [
    path('', include('server_events.urls')),
    path('admin/', admin.site.urls),
]

server_events/urls.py

from django.urls import path, include
import django_eventstream

from . import views

app_name = "server_events"
urlpatterns = [
    path("", views.index, name="index"),
    path("update", views.update),
    path(
        "events",
        include(django_eventstream.urls),
        {"channels": ["test"]}
    )
]

server_events/views.py

from django.shortcuts import render
from django.http import HttpResponse
from django_eventstream import send_event


# Create your views here.
def index(request):
    return render(request, 'server_events/index.html')


COUNTER = 0


def update(request):
    global COUNTER
    COUNTER += 1
    print("SENDING")
    send_event("test", "message", f"<p>Test: {COUNTER}<p>")
    return HttpResponse()

server_events/templates/server_events/index.html

<!DOCTYPE html>
{% load static %}
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://unpkg.com/[email protected]" integrity="sha384-rgjA7mptc2ETQqXoYC3/zJvkU7K/aP44Y+z7xQuJiVnB/422P/Ak+F/AqFR7E4Wr" crossorigin="anonymous"></script>
    <script src="https://unpkg.com/htmx.org/dist/ext/sse.js"></script>
    <link rel="stylesheet" href="https://cdn.simplecss.org/simple.css">
</head>
<body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
<h1>Hello World</h1>
  <button hx-get="update" hx-swap="none">Update</button>
  <div hx-ext="sse" sse-connect="/events" sse-swap="test">
      Contents of this box will be updated in real time
      with every SSE message received from the chatroom.
  </div>
</body>
</html>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions