I don't get it. Even though I've converted all the required fields in async await. But still I'm getting the following error:
HTTP GET /chat/8/ 200 [0.02, 127.0.0.1:51354]
WebSocket HANDSHAKING /ws/chat/8/ [127.0.0.1:51356]
Exception inside application: You cannot call this from an async context - use a thread or sync_to_async.
Traceback (most recent call last):
File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/channels/sessions.py", line 183, in __call__
return await self.inner(receive, self.send)
File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/channels/middleware.py", line 41, in coroutine_call
await inner_instance(receive, send)
File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/channels/consumer.py", line 58, in __call__
await await_many_dispatch(
File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/channels/utils.py", line 51, in await_many_dispatch
await dispatch(result)
File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/channels/consumer.py", line 73, in dispatch
await handler(message)
File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/channels/generic/websocket.py", line 175, in websocket_connect
await self.connect()
File "/media/fahadmdkamal/WORK/B-DOPS/api/chat/consumers.py", line 23, in connect
other_user = await sync_to_async(User.objects.get(id=others_id))
File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/django/db/models/manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/django/db/models/query.py", line 411, in get
num = len(clone)
File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/django/db/models/query.py", line 258, in __len__
self._fetch_all()
File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/django/db/models/query.py", line 1261, in _fetch_all
self._result_cache = list(self._iterable_class(self))
File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/django/db/models/query.py", line 57, in __iter__
results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1150, in execute_sql
cursor = self.connection.cursor()
File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/django/utils/asyncio.py", line 24, in inner
raise SynchronousOnlyOperation(message)
django.core.exceptions.SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async.
WebSocket DISCONNECT /ws/chat/8/ [127.0.0.1:51356]
The error says I need to call sync_to_async. Even though I called the function I still get this error. I wanted to print other user which was fatched from the database. But it's not even going to that statement which means my database calling might be not in proper way. I've seen some code structures of others which seems exactly same to mine. What am I missing ?
My Consumer Code:
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
# Get message sender information
self.me = self.scope['user']
# print(self.me)
# Get other user's ID from the url
others_id = self.scope['url_route']['kwargs']['user_id']
# Get Other user object using the collected object.
other_user = await sync_to_async(User.objects.get(id=others_id))
print(other_user)
# Get or create personal thread for the conversation
self.thread_obj = await sync_to_async(
Thread.objects.get_or_create_personal_thread(
self.me,
other_user)
)
# Creating room with the thread object id.
self.room_name = f'presonal_thread_{self.thread_obj.id}'
# Adding room and channel name to to the channel layer group
await self.channel_layer.group_add(
self.room_name,
self.channel_name
)
# Accecpting the connection
await self.accept()
print(f'[{self.channel_name}] - You are connected')
# Codes that will be run while disconnecting the websocket
async def disconnect(self, close_code):
print(f'[{self.channel_name}] - Disonnected')
# remove room and channel name from the channel layer
await self.channel_layer.group_discard(
self.room_name,
self.channel_name
)
# Receive message from WebSocket
async def receive(self, text_data):
# Decode json raw data that was sent from user
text_data_json = json.loads(text_data)
message = text_data_json['message']
print(f'[{self.channel_name}] - Recieved message - {message}')
# Store the received data
await self.store_message(message=message)
# Send message to room group so that it can send to the all users
await self.channel_layer.group_send(
self.room_name,
{
'type': 'chat_message',
'message': message,
}
)
# Receive message from room group
async def chat_message(self, event):
print(f'[{self.channel_name}] - Message sent - {event["message"]}')
message = event['message']
# Send message to WebSocket
await self.send(text_data=json.dumps({
'message': message,
'user_id': self.scope['user'].id,
'username': self.scope['user'].username
}))
@database_sync_to_async
def store_message(self, message):
Message.objects.create(sender=self.me,
thread=self.thread_obj,
message=message)
My Routing Code:
application = ProtocolTypeRouter({
'websocket': AuthMiddlewareStack(
URLRouter([
path('ws/chat/<int:user_id>/', consumers.ChatConsumer)
])
),
})
Since I'm new with django channels it would be even more helpful if you could say is the whole AsyncWebsocketConsumer structure is ok ?
Ohh.. Solved it..
Actually I was constructing the await sync_to_async call improperly. The function should be called seperately the params should be called in separate parenthesis.
Old and wrong Structure:
other_user = await sync_to_async(User.objects.get(id=others_id))
Updated and corrent structure:
other_user = await sync_to_async(User.objects.get)(id=others_id)
Here I'm calling the sync_to_async method to get user object in first parenthesis and in second parenthesis I'm providing the query params.