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"
}
)
Related
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!
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.
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).
I'm developing WebSocket programs in Django. I want to receive the request from the client(ReactJS) and send signals to WebSocket consumer, and then WebSocket consumer sends messages to the client.
How to solve such a problem?
I tried as following.
urls.py
urlpatterns = [
url(r'^updatesignal/$',updatesignal),
url(r'^stopsignal/$',stopsignal),
]
views.py
#api_view(['POST'])
def updatesignal(request):
print("update")
consumers.isconnected = 1
return Response("update signal")
#api_view(['POST'])
def stopsignal(request):
print("stop signal")
consumers.isconnected = 0
return Response("stop signal")
consumers.py
from channels.generic.websocket import AsyncWebsocketConsumer
import json
import asyncio
isconnected = 0
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
print("connect")
await self.accept()
while isconnected == 1:
await asyncio.sleep(2)
print("true")
# obj = # do_something (Ex: constantly query DB...)
await self.send(text_data=json.dumps({
'message': "aaa"
}))
async def disconnect(self, close_code):
# Leave room group
pass
async def receive(self, text_data):
# Receive message from WebSocket
.....
But WebSocket consumer is fired after 10 seconds after receiving update signal request. How to solve this problem?
I have a project in django 2.0 nad django-channlels 2.0 which I need to host I followed the documentation and I was able to run channels on localhost along with redis
but when I hosted on pythonanywhere,it showed it doesnot support websocket, so then I hosted on heroku,but there they were asking for verification of credit card info which i dont have to run redis.Are there additional hosting website whre I can rrun redis erver for free
Or is it poosible to implement channels without channel_layer and redis.My code is working perfectly fine on local host but can't host online for free.
class PageConsumer(WebsocketConsumer):
def connect(self, **kwargs):
self.accept()
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)("admin", {"type": "analytics.admin_message", "message": "plus"})
def disconnect(self, close_code):
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)("admin", {"type": "analytics.admin_message", "message": "minus"})
its corresponidng receiver
class ChatConsumer(WebsocketConsumer):
def connect(self, **kwargs):
self.accept()
async_to_sync(self.channel_layer.group_add)("admin", self.channel_name)
def disconnect(self, close_code):
async_to_sync(self.channel_layer.group_discard)("admin", self.channel_name)
def analytics_admin_message(self, something):
if something["message"] == "plus":
self.send(text_data=json.dumps({
'message': "plus"
}))
else:
self.send(text_data=json.dumps({
'message': "minus"
}))
def receive(self, text_data):
print("data hai bhyi", text_data)
text_data_json = json.loads(text_data)
message = text_data_json['message']
self.send(text_data=json.dumps({
'message': message
}))
settings.py
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("127.0.0.1", 6379)],
},
},
}
from the docs
Channel layers are an entirely optional part of Channels as of version 2.0. If you don’t want to use them, just leave CHANNEL_LAYERS unset, or set it to the empty dict {}.
It will mean you will be unable to use self.channel_layer in the consumer, which you rely on.
So, it's optional but you need it.
In memory exists:
CHANNEL_LAYERS={
"default": {
"BACKEND": "channels.layers.InMemoryChannelLayer"
}
}