Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Creating serializer from instance in async context #70

Closed
lautarodapin opened this issue Apr 20, 2021 · 6 comments
Closed

[BUG] Creating serializer from instance in async context #70

lautarodapin opened this issue Apr 20, 2021 · 6 comments

Comments

@lautarodapin
Copy link
Contributor

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behavior:

  1. ..
  2. ..
class Message(models.Model):
    text = models.TextField()
    user = models.ForeignKey(User, related_name="messages", on_delete=modelsCASCADE)

class MyConsumer(GenericAsyncAPIConsumer):
    queryset = Message.objects.all()
    serializer_class = MessageSerializer
    chat_room_name : Optional[str]

    @action()
    async def join_chat(self, chat: int, **kwargs):
        self.chat_room_name = f"chat_{chat}"
        await self.channel_layer.group_add(
            self.chat_room_name,
            self.channel_name,
        )
        user_serializer = await database_sync_to_async(UserSerializer)(self.scope["user"], many=False)
        await self.channel_layer.group_send(
            self.chat_room_name,
            {
                "type": "notification",
                "message": f"{self.scope['user'].username} joined the chat",
                "user": user_serializer.data,
                "chat": chat
            }
        )
    async def notification(self, event):
        content = dict(
            action="notification",
            message=event["message"],
            chat=event["chat"],
            user=event["user"],
            status=status.HTTP_200_OK
        )
        await self.send_json(content)

    

When I send the action to the websocket, it should add to a group of users, and send to each one the notification, with the key user that is the user that executes the action. BUT when i want to create the serializer in the async context, it never ends.

LOG

      # Test join chat.
        await communicator.send_json_to({
            "action": "join_chat",
            "chat": chat.pk,
            "request_id": now().timestamp(),
        })
>       response = await communicator.receive_json_from()

backend\tests\test_channels_rest_consumers.py:103:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
venv\lib\site-packages\channels\testing\websocket.py:93: in receive_json_from
    payload = await self.receive_from(timeout)
venv\lib\site-packages\channels\testing\websocket.py:72: in receive_from
    response = await self.receive_output(timeout)
venv\lib\site-packages\asgiref\testing.py:78: in receive_output
    self.future.result()
venv\lib\site-packages\channels\routing.py:150: in __call__
    return await application(
venv\lib\site-packages\channels\consumer.py:94: in app
    return await consumer(scope, receive, send)
venv\lib\site-packages\channels\consumer.py:58: in __call__
    await await_many_dispatch(
venv\lib\site-packages\channels\utils.py:51: in await_many_dispatch
    await dispatch(result)
venv\lib\site-packages\channels\consumer.py:73: in dispatch
    await handler(message)
venv\lib\site-packages\channels\generic\websocket.py:196: in websocket_receive
    await self.receive(text_data=message["text"])
venv\lib\site-packages\channels\generic\websocket.py:259: in receive
    await self.receive_json(await self.decode_json(text_data), **kwargs)
venv\lib\site-packages\djangochannelsrestframework\consumers.py:160: in receive_json
    await self.handle_action(action, request_id=request_id, **content)
venv\lib\site-packages\djangochannelsrestframework\consumers.py:151: in handle_action
    await self.handle_exception(exc, action=action, request_id=request_id)
venv\lib\site-packages\djangochannelsrestframework\consumers.py:117: in handle_exception
    raise exc
venv\lib\site-packages\djangochannelsrestframework\consumers.py:144: in handle_action
    response = await method(request_id=request_id, action=action, **kwargs)
backend\consumers.py:52: in join_chat
    "user": user_serializer.data,
venv\lib\site-packages\rest_framework\serializers.py:548: in data
    ret = super().data
venv\lib\site-packages\rest_framework\serializers.py:246: in data
    self._data = self.to_representation(self.instance)
venv\lib\site-packages\rest_framework\serializers.py:515: in to_representation
    ret[field.field_name] = field.to_representation(attribute)
venv\lib\site-packages\rest_framework\serializers.py:663: in to_representation
    return [
venv\lib\site-packages\django\db\models\query.py:280: in __iter__
    self._fetch_all()
venv\lib\site-packages\django\db\models\query.py:1324: in _fetch_all
    self._result_cache = list(self._iterable_class(self))
venv\lib\site-packages\django\db\models\query.py:51: in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
venv\lib\site-packages\django\db\models\sql\compiler.py:1167: in execute_sql
    cursor = self.connection.cursor()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _  

args = (<django.db.backends.sqlite3.base.DatabaseWrapper object at 0x0000011228A21400>,), kwargs = {}, event_loop = <_WindowsSelectorEventLoop running=False closed=False debug=False>

    @functools.wraps(func)
    def inner(*args, **kwargs):
        if not os.environ.get('DJANGO_ALLOW_ASYNC_UNSAFE'):
            # Detect a running event loop in this thread.
            try:
                event_loop = asyncio.get_event_loop()
            except RuntimeError:
                pass
            else:
                if event_loop.is_running():
>                   raise SynchronousOnlyOperation(message)
E                   django.core.exceptions.SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async.

venv\lib\site-packages\django\utils\asyncio.py:24: SynchronousOnlyOperation
  • OS: Windows 10
@hishnash
Copy link
Member

DRF does the DB query when you do user_serializer.data not on its __init__ method... the cleanest way to deal with this is to create a method that creates the UserSerializer and returns the .data.

@lautarodapin
Copy link
Contributor Author

DRF does the DB query when you do user_serializer.data not on its __init__ method... the cleanest way to deal with this is to create a method that creates the UserSerializer and returns the .data.

Ok thanks! Will try it

@hishnash
Copy link
Member

@lautarodapin did this work for you?

@lautarodapin
Copy link
Contributor Author

@lautarodapin did this work for you?

yes thanks! the easiest way was to decorate a method for serializing the current user

@database_sync_to_async
def current_user(self) -> ReturnDict:
  return UserSerializer(self.scope["user"], many=False).data

@lautarodapin
Copy link
Contributor Author

@hishnash do you have a chat or some other active contact app like slack?

I just realized that this isnt a query search xD

 @chats_messages_handler.groups_for_signal
  def chats_messages_handler(self, instance: Message, **kwargs):
      # this block of code is called very often *DO NOT make DB QUERIES HERE*
      yield f'-chat___________{instance.chat_id}'

  @chats_messages_handler.groups_for_consumer
  def chats_messages_handler(self, chat: int, **kwargs):
      # This is called when you subscribe/unsubscribe
      if chat is not None:
          yield f'-chat___________{chat}'

@hishnash
Copy link
Member

@lautarodapin you can DM me on twitter https://twitter.com/hishnash

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants