Search code examples
pythondjangodjango-channels

What's the correct way to get websocket message do display in django template using django channels?


I'm trying to display stock market data from a third party api in realtime using channels and celery. I'm using celery to get data from the api and channels to receive and send the data to the client.

My issue is that the data isn't rendering in the template and my python and javascript aren't showing any errors so I have no idea what's wrong. Help would be appreciated.

ignore commented out code in snippets.

tasks.py

import requests
from celery import shared_task
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync


channel_layer = get_channel_layer()

@shared_task
def get_stock_info ():

    # request.session['sym'] = sym
    payload = {'symbol': 'nvda'}
    # r = requests.get('https://api.twelvedata.com/time_series?&interval=1min&apikey=xxxxxxxxx',
    #  params=payload)
    r = requests.get('https://api.twelvedata.com/price?apikey=xxxxxxxxx',
     params=payload)
    res = r.json()
    price = res['price']

    async_to_sync(channel_layer.group_send)('stocks', {'type': 'send_info', 'text': price})

    

consumers.py

from channels.generic.websocket import AsyncWebsocketConsumer

class StockConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        await self.channel_layer.group_add('stocks', self.channel_name)
        await self.accept()

    async def disconnect(self):
        await self.channel_layer.group_discard('stocks', self.channel_name)

    async def send_info(self, event):
        msg = event['text']

        await self.send(msg)

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
    <title>Document</title>
</head>
<body>
   
  <p id="#price">{{ text }}</p>
  <p id="description">{{ res.Description }}</p>
  


  <script>
    var socket = new WebSocket('ws://localhost:8000/ws/stock_info/');
  
    socket.onmessage = function(event){
      var price = event.data;
  
      document.querySelector('#price').innerText = price;
  
  
    }
  </script>
  
</body>


</html>

routing.py

from django.urls import path
from .consumers import StockConsumer

ws_urlpatterns = [
    path('ws/stock_info/', StockConsumer.as_asgi())
]

celery.py

import os
from celery import Celery

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'stockwatch.settings')

app = Celery('stockwatch')
app.config_from_object('django.conf:settings', namespace='CELERY')

# app.conf.beat_schedule = {
#     'get_info_1s': {
#         'task': 'stock_info.tasks.get_stock_info',
#         'schedule': 3.0
#     }
# }

app.autodiscover_tasks()

Solution

  • This was my mistake.

    <p id="#price">{{ text }}</p>
    

    Should be

    <p id="price">{{ text }}</p>