I'm trying to get a simple Django Web App with WebSockets going, and can't get the routing to work. I have a basic Consumer, ProgressConsumer
, that is used as base for two other Consumers.
class ProgressConsumer(WebsocketConsumer):
max_anims = None
def connect(self):
# self.set_max_anims()
self.log_cache = ""
print("Websocket connected.")
if (not self.max_anims):
raise NotSetError("max_anims not set")
self.room_group_name = "progress"
self.accept()
self.start_progress_update()
def start_progress_update(self):
cached_progress = 0
progress = 0
while progress != 100.0:
time.sleep(1)
if (updates := self.get_log_updates()) == "":
continue
if "Error" in updates:
self.send(json.dumps({"error": updates}))
self.clean_log()
self.close(3000)
return
if (progress := self.calculate_progress(updates)) == cached_progress:
continue
self.send(json.dumps({
'type': 'progress',
'message': progress,
}))
cached_progress = progress
def disconnect(self, close_code):
print("Websocket disconnected. Code: ", close_code)
def get_log_updates(self) -> str:
if self.log_cache == self.get_log_cache():
return ""
s1 = set(self.log_cache.split("\n"))
s2 = set(self.get_log_cache().split("\n"))
self.log_cache = self.get_log_cache()
return str(s1.symmetric_difference(s2))
def calculate_progress(self, difference: str) -> float:
if not "Animation" in difference:
return 0.0
animation_id = float(re.search("Animation (\d )", difference).group(1))
return ((animation_id 1) / self.max_anims) * 100.0
def clean_log(self):
raise NotImplementedError("clean_log not implemented")
def get_log_cache(self) -> str:
raise NotImplementedError("get_log_cache not implemented")
The two other consumers are:
class FunctionImagesProgressConsumer(ProgressConsumer):
max_anims = 4
def clean_log(self):
with open("function_images/project_files/logs/_ComplexFunctionScene.log", "w") as f:
f.write("")
def get_log_cache(self) -> str:
with open("function_images/project_files/logs/_ComplexFunctionScene.log", "r") as f:
return f.read()
class AnalysisProgressConsumer(ProgressConsumer):
max_anims = 80
def clean_log(self):
with open("analysis_generator/project_files/logs/_AnalysisScene.log", "w") as f:
f.write("")
def get_log_cache(self) -> str:
with open("analysis_generator/project_files/logs/_AnalysisScene.log", "r") as f:
return f.read()
These two consumers are used in different apps. One app is under the URL http://127.0.0.1:8000/function_images/
, the other one http://127.0.0.1:8000/analysis_generator/
.
Now I'm trying to route the correct consumer based on what site the user is on. This is my routing.py:
from django.urls import re_path
from function_images.consumers import FunctionImagesProgressConsumer
from analysis_generator.consumers import AnalysisProgressConsumer
websocket_urlpatterns = [
re_path(r"function_images/ws/progress", FunctionImagesProgressConsumer.as_asgi()),
re_path(r"analysis_generator/ws/progress", AnalysisProgressConsumer.as_asgi()),
]
and asgi.py
:
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from .routing import websocket_urlpatterns
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dq_playground.settings')
django_asgi_api = get_asgi_application()
application = ProtocolTypeRouter({
"http": django_asgi_api,
"websocket": AuthMiddlewareStack(
URLRouter(
websocket_urlpatterns
)
)
})
However, when I run this, and attempt to open the websocket, this error occurs:
File "C:\Users\damix\AppData\Local\Programs\Python\Python310\lib\site-packages\channels\staticfiles.py", line 44, in __call__
return await self.application(scope, receive, send)
File "C:\Users\damix\AppData\Local\Programs\Python\Python310\lib\site-packages\channels\routing.py", line 71, in __call__
return await application(scope, receive, send)
File "C:\Users\damix\AppData\Local\Programs\Python\Python310\lib\site-packages\channels\sessions.py", line 47, in __call__
return await self.inner(dict(scope, cookies=cookies), receive, send)
File "C:\Users\damix\AppData\Local\Programs\Python\Python310\lib\site-packages\channels\sessions.py", line 263, in __call__
return await self.inner(wrapper.scope, receive, wrapper.send)
File "C:\Users\damix\AppData\Local\Programs\Python\Python310\lib\site-packages\channels\auth.py", line 185, in __call__
return await super().__call__(scope, receive, send)
File "C:\Users\damix\AppData\Local\Programs\Python\Python310\lib\site-packages\channels\middleware.py", line 26, in __call__
return await self.inner(scope, receive, send)
File "C:\Users\damix\AppData\Local\Programs\Python\Python310\lib\site-packages\channels\routing.py", line 168, in __call__
raise ValueError("No route found for path %r." % path)
ValueError: No route found for path 'ws/progress/'.
And here's the frontend:
const establishSocketConnection = () => {
const socket_url = `ws://${window.location.host}/ws/progress/`;
let socket = new WebSocket(socket_url);
socket.onmessage = (event: MessageEvent<any>) => {
const data = JSON.parse(event.data);
if (data.type != "progress") {
return;
}
const progress: number = data.message;
console.log(`Progress: ${progress}`);
updateProgressBar(progress);
}
socket.onclose = (): void => {
console.log("Socket connection closed");
}
socket.onerror = (error: Event): void => {
console.log("Socket error: " error);
}
return socket;
}
I'm not sure how to do the routing differently, as this should work in my eyes.
CodePudding user response:
In the frontend Javascript, you have to mention the django app name with full url like this. Simply you can call the ws request as below.
const socket_url_1 = `ws://${window.location.host}/analysis_generator/ws/progress/`;
const socket_url_2 = `ws://${window.location.host}/function_images/ws/progress/`;