Django Channels: Data Going to the Wrong Socket - django

I am facing the same issue although the as_asgi method is called in my routing. The data are always sent to the second socket.
Channels V3.0 and Django V3.1.2 is in use.
routing.py
websocket_urlpatterns = [
re_path(WS_PREFIX + r'room/(?P<room_name>\w+)/$', RoomConsumer().as_asgi()),
]
asgi.py
import routing
django_asgi_app = get_asgi_application()
application = ProtocolTypeRouter({
"http": get_asgi_application(),
# Just HTTP for now. (We can add other protocols later.)
"websocket": AuthMiddlewareStack(
URLRouter(
routing.websocket_urlpatterns
)
),
})
Local server logs:
WebSocket HANDSHAKING /ws/room/2/ [127.0.0.1:63280]
WebSocket CONNECT /ws/room/2/ [127.0.0.1:63280]
WebSocket HANDSHAKING /ws/room/1/ [127.0.0.1:63288]
WebSocket CONNECT /ws/room/1/ [127.0.0.1:63288]
consumer.py:
import json
from channels.generic.websocket import AsyncWebsocketConsumer
class RoomConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'room_%s' % self.room_name
# Join room group
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
# Leave room group
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
# Receive message from WebSocket
async def receive(self, text_data):
text_data_json = json.loads(text_data)
# Send message to room group
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'send_data',
'message': "Message received"
}
)
# Receive message from room group
async def send_data(self, event):
# Send message to WebSocket
await self.send(text_data=json.dumps(event))
This is how I send data:
result['type'] = 'send_data'
result['test'] = 'test'
layer = get_channel_layer()
async_to_sync(layer.group_send)('room_2', result)
return HttpResponse(status=202)
Is there anything else I need to consider?

I just realized that there is a newer version of channels available. I updated to channels V3.0.2 and it is working correctly now.

Related

Django Channels: Send message from outside Consumer Class

I'm new in Python and Django,
Currently, I need to setup a WebSocket server using Channels.
I follow the code in this link: Send message using Django Channels from outside Consumer class
setting.py
ASGI_APPLICATION = 'myapp.asgi.application'
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels.layers.InMemoryChannelLayer',
},
}
Here is the code Consumer
import json
from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync
class ZzConsumer(WebsocketConsumer):
def connect(self):
self.room_group_name = 'test'
async_to_sync(self.channel_layer.group_add)(
self.room_group_name,
self.channel_name
)
self.accept()
def disconnect(self, code):
async_to_sync(self.channel_layer.group_discard)(
self.room_group_name,
self.channel_name
)
print("DISCONNECED CODE: ",code)
def receive(self, text_data=None, bytes_data=None):
print(" MESSAGE RECEIVED")
data = json.loads(text_data)
message = data['message']
async_to_sync(self.channel_layer.group_send)(
self.room_group_name,
{
"type": 'chat_message',
"message": message
}
)
def chat_message(self, event):
print("EVENT TRIGERED")
# Receive message from room group
message = event['message']
# Send message to WebSocket
self.send(text_data=json.dumps({
'type': 'chat',
'message': message
}))
And outside the Consumer:
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
'test',
{
'type': 'chat_message',
'message': "event_trigered_from_views"
}
)
The expected logics is I can received the data from the group_send in the receive on the Consumer class. So that I can send message to client.
However, It's not.
Can anyone here know what's I missing?
Any help is very appreciated.
Thanks!
Updated:
routing.py
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'ws/socket-server/', consumers.ZzConsumer.as_asgi())
]
I think you're missing type argument in chat_message method. It should match the type in group_send. I.e.:
def chat_message(self, event, type='chat_message'):
print("EVENT TRIGERED")
Matches:
async_to_sync(channel_layer.group_send)(
'test',
{
'type': 'chat_message',
'message': "event_trigered_from_views"
}
)

Send disconnect message to WebSocket in Django Channels

I am using Django==3.2.5 and channels==3.0.4. In the consumers.py I am using WebsocketConsumer class. My disconnect() method is executed After disconnecting the socket, which means the disconnect method is unable to send a response to the socket. I found a similar issue in this link but they are using AsyncWebsocketConsumer class. Is there a way to solve it using WebsocketConsumer class?
I have the following code:
class MeetingStatus_Consumer(WebsocketConsumer):
def connect(self):
self.room_name = self.scope["url_route"]["kwargs"]["id"]
self.room_group_name = 'Booking_id_%s' % self.room_name
# Adding room_name and group_name to the channel
async_to_sync(self.channel_layer.group_add)(
self.room_group_name,
self.channel_name
)
self.accept() # accept the request from frontend
self.send(text_data=json.dumps({'status': "Websocket Connected"})) # send data to frontend
def disconnect(self, *args, **kwargs):
async_to_sync(self.channel_layer.group_discard)(
self.room_group_name,
self.channel_name
)
self.send(text_data=json.dumps({'status': "Websocket disconnected"}))

Send value from __init__.py to consumer.py websocket Django channels

I want to send data from the firebase stream(pyrebase) via django channels websocket. Web scoket is working fine, But I can't imagine how I can use consumer.py file send message function in init_.py.
Here my django channel web socket Consumer.py file.
import json
from random import randint
from asyncio import sleep
from channels.generic.websocket import AsyncWebsocketConsumer
from django.conf import settings
class WSConsumer(AsyncWebsocketConsumer):
group_name = settings.STREAM_SOCKET_GROUP_NAME
async def connect(self):
# Joining group
await self.channel_layer.group_add(
self.group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
# Leave group
await self.channel_layer.group_discard(
self.group_name,
self.channel_name
)
async def receive(self, text_data):
print(text_data)
# Send data to group
await self.channel_layer.group_send(
self.group_name,
{
'type': 'system_load',
'data': text_data
}
)
async def system_load(self, event):
# Receive data from group
print('sending message to client')
await self.send(text_data=json.dumps(event['data']))
This file works fine.
This is my inti.py file. In this file, I want to send data to the WebSocket in this stream_handler function.
import pyrebase
from configFiles.config import config
firebase = pyrebase.initialize_app(config)
authe = firebase.auth()
database = firebase.database()
safe_temperature = 20
safe_humidity = 40
def stream_handler(message):
if "Humidity" in message["path"]:
if message["data"] > safe_humidity:
database.child("Controller").child("Light").set(1)
else:
database.child("Controller").child("Light").set(0)
if "Temperature" in message["path"]:
if message["data"] > safe_humidity:
database.child("Controller").child("Fan").set(1)
else:
database.child("Controller").child("Fan").set(0)
# Here place I want to send data which is message["data"] to the WebSocket
mystream = database.child("Sensor").stream(stream_handler)
Thank you!

Django: Query database from Channels consumers

I had a Django app that had live chat working. Now I am trying to add a query to the database within the connect method. I am following the Channels documentation, and tried the solution in this other StackOverflow question but nothing is working.
Below is my code. The error I'm seeing in the Javascript console is `WebSocket connection to 'ws://localhost:9008/ws/chat/334/' failed: WebSocket is closed before the connection is established. I do have redis-server running on localhost and that was working before, so that's not a problem.
async def connect(self):
print('connect (got here!)')
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'chat_%s' % self.room_name
print('self.room_name: ' + str(self.room_name))
valid_connection = await database_sync_to_async(self.verify_chat_room_key)()
print('valid_connection: ' + str(valid_connection))
# Join room group
# async_to_sync(self.channel_layer.group_add)(
# self.room_group_name,
# self.channel_name
# )
await self.accept()
def verify_chat_room_key(self):
print('~~~in verify method...~~~')
return True
Edit: Below is the stacktrace showing the python debugs:
HTTP GET /confirmed-trade/334/ 200 [1.28, 127.0.0.1:53877]
WebSocket HANDSHAKING /ws/chat/334/ [127.0.0.1:58882]
HTTP GET /static/favicon.png 200 [0.01, 127.0.0.1:53877]
WebSocket HANDSHAKING /ws/chat/334/ [127.0.0.1:59246]
WebSocket DISCONNECT /ws/chat/334/ [127.0.0.1:59246]
disconnect
WebSocket HANDSHAKING /ws/chat/334/ [127.0.0.1:65175]
WebSocket DISCONNECT /ws/chat/334/ [127.0.0.1:58882]
disconnect
WebSocket DISCONNECT /ws/chat/334/ [127.0.0.1:65175]
disconnect
self.accept() is asynchronous, so it should be:
await self.accept()
from channels.db import database_sync_to_async
class ChatConsumer(AsyncWebsocketConsumer):
""" handshake websocket front end """
room_name = None
room_group_name = None
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'chat_%s' % self.room_name
"""Creating a new variable """
self.botname = await database_sync_to_async(self.get_name)()
print(self.botname)
# Join room group
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
""" Creating a Function """
def get_name(self):
return CustomUser.objects.filter(groups__id = self.room_name).filter(is_bot = True)[0].username
may be, helpful for you

Django channels live chat save sent messages

So I have a Django app in which I used channels to implement live chat. My consumer looks like this:
import json
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
class ChatConsumer(WebsocketConsumer):
def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'chat_%s' % self.room_name
# Join room group
async_to_sync(self.channel_layer.group_add)(
self.room_group_name,
self.channel_name
)
self.accept()
def disconnect(self, close_code):
# Leave room group
async_to_sync(self.channel_layer.group_discard)(
self.room_group_name,
self.channel_name
)
# Receive message from WebSocket
def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
username = self.scope["user"]
# Send message to room group
async_to_sync(self.channel_layer.group_send)(
self.room_group_name,
{
'type': 'chat_message',
'message': message,
'user': username.username
}
)
# Receive message from room group
def chat_message(self, event):
message = event['message']
user=event['user']
print(user)
# Send message to WebSocket
self.send(text_data=json.dumps({
'message': message,
'user':user
}))
So I am looking for a way to save the sent messages (because currently they are lost on refresh). I have created a Messages model that has a CharField for the message text. I think I can save new messages if I do this in my chat_message function:
new_message=Messages(text=message)
new_nessage.save()
My question is how do I preload the last 10 messages whenever a user gets connected to the chat?
So in order to do this, I ended up saving my message to the database in the "receive" function (because if you save it in the chat_message one it's saved 1 time for each active user). Then in order to preload messages I used an AJAX call every time a websocket is opened in order to get the 10 most recent messages using a python function and then I passed the messages back as a JsonResponse (https://simpleisbetterthancomplex.com/tutorial/2016/08/29/how-to-work-with-ajax-request-with-django.html scroll down to the AJAX requests part of this article and take a look at signup.html, urls.py and views.py files in this order for reference).