Hi,
Im after some advice.
I want to build a kind of terminal app, where content will be loaded in the screen in realtime, or as close as. This will be based on comments from users. bit like a chat system.
I’ve looked and django channels, but not clear on how i get the output to the screen?
So wasn’t sure if anyone could point me in the right direction?
Thanks
Tom
I have used the websockets
library in the past https://websockets.readthedocs.io/ to connect to the server endpoint implemented using Django channels. Other libraries such as httpx-ws exist but I haven’t used them. You could use such a library to implement a terminal program.
If you want to use a webbrowser as a client you could start here:
A different way to go would be to use SSE (server-sent events) instead of Websockets, if you don’t need bidirectional communication:
Many possibilities as always!
Thanks for the reply, matthiask.
Server Side Events might be the way to go. When the comments come in i want to be able to make them as complete for example. So i was hoping to render a button on the screen. Would Server Side Events allow this?
Sorry, if thats a stupid question, its just i’ve never worked on a terminal like interface before
Yes, sure. You’d have to send the completion event as a form post though, you cannot use the SSE channel because SSE is unidirectional.
Ok, so post the form back to mark the comment as completed, and if i was to have a field on my model, as completed true/false then i could reflect this on the page being fed back from the SSE?
That depends upon what the data is that you are sending back through the channel.
If you look at the Django Channels chat tutorial, it’s sending text - which the JavaScript client adds to a div.
I use HTMX with the websocket extension. That allows me to send an HTML element from the server, and HTMX will then inject it into the corresponding spot on the page.
A basic proof-of-concept example using the chat tutorial as a base is located at GitHub - KenWhitesell/htmx_channels.
I also did a talk on WebRTC with Django, Channels, and HTMX at this past DjangoCon - https://youtu.be/8EJzcpw8i8s
This is a more comprehensive example of a real-life use of HTMX and Channels for displaying live information.
But you don’t need to use HTMX for this. The key point of any of this is that it’s just data coming through the websocket, and it’s up to you to provide the JavaScript that knows what to do with it.
Hi, Ken.
Ill check out the links. Im only looking to render text back to the screen it will be rended using bootstrap so it looks nice hopefully 
Basically when someone adds a comment i want to render that in the terminal/screen so someone can view realtime.
The data is just a field in my model.
I’ve managed to get websockets running locally, which is really cool. But when i run this in production using https i get the error
terminal.js:29 WebSocket closed unexpectedly
Im not sure how to troubleshoot this?
I’m running with gunicorn, confused if I need to use Daphne?
I’m running this in a container in Kubernetes, if that makes any difference?
Thanks
Tom
You absolutely need to use Daphne (or some other async-based server such as uvicorn) with Channels listeners.
If you’re using Daphne in development as described in the docs, you’ll see that when you’re testing with runserver
, it’s actually running an instance of Daphne.
Additionally, if you’re running this project behind nginx, you’ll want to split out the websocket url from the regular Django url so that you can apply different nginx directives for each kind on connection (websocket vs standard http).
Finally, what we do is run two different servers for the two types of connections. We run uwsgi for the regular Django work and Daphne for the websockets.
If you search through the forum, both this topic and deployment, you’ll find a number of threads discussing this.
Regarding Kubernetes, it shouldn’t matter. We do this in one project using Docker containers and it works great. I have no reason to believe that adding K8s into the mix fundamentally changes anything.
So if I install channels[daphne] would that allow me to run both on the single server? Would I still need to create a separate route on my ingress controller or can I send both ws and http traffic on the same app, I’m assuming that when it makes the ws connection this is client side and therefore requires a separate route to wss/mywebsocketserver?
You can do it that way, yes. I prefer not to because we don’t use asynchronous views and consider the synchronous server (uwsgi) to be the safer and better-known choice.
Yes.
Technically, they’re still fundamentally “HTTP” requests, but how they are handled internally are different.
Yes, it’s JavaScript that opens the websocket connection. (See Writing WebSocket client applications - Web APIs | MDN for more details on this.)
Beyond that, I’m not quite sure what you’re asking here.
In the general case, you would use nginx (or Apache) to handle static and media files directly, route the websocket url to the Channels consumer, and everything else to your Django wsgi container. If you service them both using Daphne, you will still want the separate paths defined in nginx, but also configuring a Django Channels router.
Ok. So I am a bit confused on the ingress. I’m using azure application gateway as my ingress controller. It has native support for websockets
I have a route on my ingress ‘https://mysite.com’
What would the url be that I need to add for websockets.
As all traffic https should go to the app,
Or would I create a route wss://mysite.com. I think this is what I need to do?
I’m sorry, I can’t help you here specifically.
In the abstract, you’ve got two different types of HTTP connections being made.
One is what I’ll call standard HTTP, which is the traditional request followed by a traditional response. The other is the websocket upgrade request, which superficially “looks” like HTTP on the protocol layer, but needs to be handled differently by the backend.
How you set this up to be segregated and managed is up to you.
In my case, I use nginx to receive all requests. It proxies everything in the /ws/
location to Daphne for a websocket. The /static/
and /media/
locations are handled directly. Everything else is proxied to uwsgi as a standard Django app.
I’m not sure I understand what you mean by:
What “route” are you talking about?
Yes, in your JavaScript you generally reference it as wss://mysite.com
. However, it’s still fundamentally an HTTP connection, and nginx sees it coming in as an HTTP request.
Recognizing this, WebSocket: WebSocket() constructor - Web APIs | MDN shows that the url
parameter in the JavaScript WebSocket()
constructor now allows https
, http
, and relative URLs to be used instead of the wss://
or ws://
forms.
For more details on this, you may also want to see RFC 6455: The WebSocket Protocol, particularly section 1.3, for details on the WebSocket connection request.
Thanks again, Ken.
I’ve looked through the applications gateway docs for websockets and it’s says i create a route ws.mysite.com point to my app. So I’m going to give this a go. Hopefully this is what is blocking the ws connection from establishing
Ok, so i was mistaken, I dont need to add an additonal route as i already have a route on my ingress for https traffic going to my django app.
So im not sure what is stopping the connection from being established.
Do you offload the certificate on nginx so that you hit the frontend on 443 and then pass the traffic to the app over 80?
Or do you have full ssl all the way?
I terminate the ssl on mine so i’m wondering if this is causing the problem?
Yes. There are very few circumstances (actually, only 1 truly legitimate and 1 quasi-reasonable that I can think of off-hand) where it’s beneficitial to require uwsgi / Daphne / gunicorn / etc to handle the SSL layer. (There may be more than that, I’m just not immediately thinking or aware of more.)
It can be done - so doing it isn’t the issue. However, everything does need to be configured correctly - and when it’s not, there’s generally some kind of error condition thrown.
I’ve now deployed my app to standard webapp with no app gateway or ssl offload involved and it’s still not working, so i think this suggest it must be configuration somewhere i have missed.
Hard to know where because it works locally.
is this correct:
application = ProtocolTypeRouter({
'http': get_asgi_application(),
'websocket': AuthMiddlewareStack(
URLRouter(
terminal.routing.websocket_urlpatterns
)
),
})
Or should i be using https: get_asgi_application()
when in production?
The protocol is http.
But you really shouldn’t be looking at these separate pieces independently.
They are all interrelated, and every setting needs to be evaluated with respect to all the other settings. There are many different combinations of things that will work - assuming they’re all coordinated.
Do i need to run a different startup command when running Daphne
and gunicorn
gunicorn -w 2 -b 0.0.0.0:8000 my_app.wsgi:application