database_sync_to_async not working in django channels consumers - django

This is my consumers file and I am using AsyncWebSocketConsumer. And Get details function is not working.
class NotificationConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['username']
self.room_group_name = 'notification_%s' % self.room_name
notificationobjs=await self.get_details(self.room_name)
# unseencount=application_notifications.objects.filter(User=User.objects.get(username=self.room_name),is_seen=False).count()
data={'notifications':NotificationSerializer(notificationobjs,many=True).data,'count':0}
print(self.room_group_name)
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
await self.send(text_data=json.dumps({'status':'connected','user':self.room_group_name,'payload':data}))
#database_sync_to_async
def get_details(self,room_name):
return application_notifications.objects.filter(User=User.objects.get(username=room_name))
Its Showing the error "django.core.exceptions.SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async."
If Anyone can help me solve this issue, It would be highly appreciated. Thanks

Related

Django Websocket getting slow as the connected users increases

I am using Django Channels to send messages to the frontend. and the frequency of the messages is one message per second, everything works fine when I am working on local but on production where I used Nginx, Gunicorn and Daphne to host the server, the connection to the websocket happened after 2-3 attempts, and after the webscokets get connected the messages started coming really slow. Is this issue is related to the server or with the code ?
consumer.py
class MyConsumer(AsyncJsonWebsocketConsumer):
async def connect(self):
self.room_group_name = 'Live Message'
self.error_code = 4011
await self.channel_layer.group_add(self.room_group_name, self.channel_name)
await self.accept()
async def disconnect(self, code):
await self.channel_layer.group_discard(self.room_group_name, self.channel_name)
await self.close(self.error_code)
async def send_live_data(self, event):
data = event.get('value')
additional_msg = get_additional_msg()
data['additional_msg'] = json.dumps(additional_msg, default=default)
try:
await self.send_json(data)
except Exception as e:
await self.disconnect({'code': self.error_code})
await self.close(self.error_code)
message.py
channel_layer = get_channel_layer()
async def send_data():
try:
await (channel_layer.group_send)('Live Message', {
'type': 'send_live_data',
'value': {'live_message':json.dumps(message)}})
except:
print("Error while sending message ...\n", traceback.format_exc())
class MyWorker(Thread):
def __init__(self):
Thread.__init__(self)
def run(self):
print('Worker is running .....')
while True:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(send_data())
loop.close()
time.sleep(1)
MyWorker().start()
Thank you !!!

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"}))

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

How to implement multichat in Django channels

I am implementing a real-time chat functionality using Django channels 3 and Django rest framework, where I have two types of users clients and admins , what I am trying to do is to create for every user a chat room in order to discuss with the admins, I tried to use multiple rooms and it doesn't work because the admin is unaware of the newly created user room , my second choice is to use room_group_name , but so far I don't really understand it, if someone can clarify or help I would be grateful .
Consumer.py
async def connect(self):
self.user = self.scope["user"]
print(self.user.is_superuser)
print(self.user.is_staff)
if self.user.is_superuser:
self.room_name = "chat"
self.room_group_name = 'admin_%s' % self.room_name
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
if self.user.is_client:
self.room_name = "chat"
self.room_group_name = 'client_%s'.format(self.user) % self.room_name
admin = 'admin_%s' % self.room_name
await self.channel_layer.group_add(
# self.room_group_name,
self.room_group_name,
self.channel_name
)
await self.accept()
So far I can separate the two groups, but the trick is how to add admin users to the client group room ??

Django Channels: Data Going to the Wrong Socket

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.