Home > Net >  Django WebSocket Routing "Not route for path"
Django WebSocket Routing "Not route for path"

Time:07-02

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/`;
  • Related