1

I'm trying to get the profile firstname but it gives me this error

File "/home/marwan/Desktop/Gowd/venv/lib/python3.6/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 /public_chat/1/ [127.0.0.1:53742]
INFO 2021-03-12 01:43:30,570 runserver 119412 140507688003328 WebSocket DISCONNECT /public_chat/1/ [127.0.0.1:53742]

I think that this error has something to do with trying to get the user profile in line 56

"profile":   self.scope["user"].profile.firstname,

if I remove it will work fine

this is my consumers.py file

from channels.generic.websocket import AsyncJsonWebsocketConsumer
import json
from django.contrib.auth import get_user_model
from gowd_project.users.models import User, Profile

User = get_user_model()


# Example taken from:
# https://github.com/andrewgodwin/channels-examples/blob/master/multichat/chat/consumers.py
class PublicChatConsumer(AsyncJsonWebsocketConsumer):

    async def connect(self):
        """
        Called when the websocket is handshaking as part of initial connection.
        """
        print("PublicChatConsumer: connect: " + str(self.scope["user"]))
        # let everyone connect. But limit read/write to authenticated users
        await self.accept()

        # Add them to the group so they get room messages
        await self.channel_layer.group_add(
            "public_chatroom_1",
            self.channel_name,
        )

    async def disconnect(self, code):
        """
        Called when the WebSocket closes for any reason.
        """
        # leave the room
        print("PublicChatConsumer: disconnect")
        pass

    async def receive_json(self, content):
        """
        Called when we get a text frame. Channels will JSON-decode the payload
        for us and pass it as the first argument.
        """
        # Messages will have a "command" key we can switch on
        command = content.get("command", None)
        print("PublicChatConsumer: receive_json: " + str(command))
        print("PublicChatConsumer: receive_json: message: " + str(content["message"]))
        if command == "send":
            if len(content["message"].lstrip()) == 0:
                raise Exception("You can't send an empty message.")
            await self.send_message(content["message"])

    async def send_message(self, message):
        await self.channel_layer.group_send(
            "public_chatroom_1",
            {
                "type": "chat.message",
                "user_id": self.scope["user"].id,
                "user_name": self.scope["user"].name,
                "profile":   self.scope["user"].profile.firstname,
                "message": message,
            }
        )

    async def chat_message(self, event):
        """
        Called when someone has messaged our chat.
        """
        # Send a message down to the client
        print("PublicChatConsumer: chat_message from user #" + str(event["user_id"]))
        await self.send_json(
            {
                "user_id": event["user_id"],
                "user_name": event["user_name"],
                "message": event["message"],
            },
        )
  • Is that the _full_ stack trace? If removing that line makes it work fine, then the error pretty clearly states that something you're attempting there -- I'd guess the DB call to fetch the profile object -- is not allowed in an async method. I don't know how `self.scope["user"]` is being populated, but if you have control over that, using [`select_related`](https://docs.djangoproject.com/en/3.1/ref/models/querysets/#django.db.models.query.QuerySet.select_related) to fetch profile data along with the user might solve your problem. Also, you shouldn't import User if you're going to reset it. – kungphu Mar 12 '21 at 02:49
  • I think that's right it is not allowed in an async method so who do you suggest I can get the user profile in this case? can you clear it more because I am new to this stuffs – Marwan Akram Mar 12 '21 at 03:06
  • The error message suggests that you "use a thread or sync_to_async" to get around this. You should look into that and check out `select_related`, linked in my first comment, to see whether any of those work for you. I don't know which, if any, would work in your situation, so that's research you'll need to do based on your requirements. – kungphu Mar 12 '21 at 03:16

1 Answers1

0

sync_to_async takes a callable, not the result. so, I need to do this:

@sync_to_async
    def get_user_profile(self, user):
        return user.profile

    async def send_message(self, message):
        profile = await PublicChatConsumer.get_user_profile(self.scope["user"])
        profile_name = profile.firstname
        await self.channel_layer.group_send(
            "public_chatroom_1",
            {
                "type": "chat.message",
                "user_id": self.scope["user"].id,
                "username": profile_name,
                "message": message,
            }
        )

full documentation Similar problem