Чаты сигналов и каналов Django - PullRequest
0 голосов
/ 11 мая 2018

Привет, я борюсь за то, чтобы две вещи работали одновременно ...

Пример channel2-чата был в порядке, но я хотел добавить возможность узнать, какмного людей где в комнате.Я сделал это, обновив модель комнат.

Затем я захотел иметь панель управления, показывающую наиболее популярные текущие комнаты, которую я снова хотел обновить с использованием каналов.Я использовал метод сигналов django, и этот метод работал для обновления модели, пока никто не использовал чат.

Однако, при проверке обновления панели управления, когда кто-то присоединился к чату, была ошибка.

    2018-05-11 19:19:09,634 - ERROR - server - Exception inside application: You cannot use AsyncToSync in the same thread as an async event loop - just await the async function directly.
  File "/dev/channels_sk/channels-master/channels/consumer.py", line 54, in __call__
    await await_many_dispatch([receive, self.channel_receive], self.dispatch)
  File "/dev/channels_sk/channels-master/channels/utils.py", line 50, in await_many_dispatch
    await dispatch(result)
  File "/dev/channels_sk/channels-master/channels/consumer.py", line 67, in dispatch
    await handler(message)
  File "/dev/channels_sk/channels-master/channels/generic/websocket.py", line 173, in websocket_connect
    await self.connect()
  File "/dev/channels_sk/tables/consumers.py", line 19, in connect
    room.save()
  File "/dev/channels_sk/.env/lib/python3.6/site-packages/django/db/models/base.py", line 729, in save
    force_update=force_update, update_fields=update_fields)
  File "/dev/channels_sk/.env/lib/python3.6/site-packages/django/db/models/base.py", line 769, in save_base
    update_fields=update_fields, raw=raw, using=using,
  File "/dev/channels_sk/.env/lib/python3.6/site-packages/django/dispatch/dispatcher.py", line 178, in send
    for receiver in self._live_receivers(sender)
  File "/dev/channels_sk/.env/lib/python3.6/site-packages/django/dispatch/dispatcher.py", line 178, in <listcomp>
    for receiver in self._live_receivers(sender)
  File "/dev/channels_sk/tables/signals.py", line 20, in room_save_handler
    'update': instance.population,
  File "/dev/channels_sk/.env/lib/python3.6/site-packages/asgiref/sync.py", line 34, in __call__
    "You cannot use AsyncToSync in the same thread as an async event loop - "
  You cannot use AsyncToSync in the same thread as an async event loop - just await the async function directly. 

consumer.py

from channels.generic.websocket import AsyncWebsocketConsumer
import json
import time
from .models import Room
from django.db.models import F

class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = 'chat_%s' % self.room_name

        # create room or increment
        room, created = Room.objects.get_or_create(title=self.room_name)
        pop = room.population + 1
        room.population = F('population') + 1
        room.save()

        # Join room group
        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )

        await self.accept()

        # send new population to group
        await self.channel_layer.group_send(
            self.room_group_name,
            {
            'type': 'pop_message',
            'population': pop,
            }
        )

    async def disconnect(self, close_code):
        room = Room.objects.get(title=self.room_name)
        pop = room.population - 1
        if room.population == 1:
            if room.permanent == False:
                room.delete()
        else:
            room.population = F('population') - 1
            room.save()

        # send new population to room group
        await self.channel_layer.group_send(
            self.room_group_name,
            {
            'type': 'pop_message',
            'population': pop,
            }
        )

        # 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)
        message = text_data_json['message']


        # Send message to room group
        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'chat_message',
                'message': message,
        )

    # Receive message from room group
    async def chat_message(self, event):
        message = event['message']

        # Send message to WebSocket
        await self.send(text_data=json.dumps({
            # 'type': 'chat_message',
            'message': message,
        }))

    # change in room group population
    async def pop_message(self, event):
        content = event['type']
        population = event['population']

        # Send message to WebSocket
        await self.send(text_data=json.dumps({
            # 'type': 'pop_message',
            'content': content,
            'population': population,

        }))


class RoomConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.group_name = 'chat_room_dash'
        print("joined dash room")

        # Join room group
        await self.channel_layer.group_add(
            self.group_name,
            self.channel_name
        )

        await self.accept()

    async def disconnect(self, close_code):
        print("left dash room")
        pass

    async def send_message(self, text_data=None):
        print(text_data)
        labels = []
        data = []
        for room in Room.objects.all()[:3]:
            labels.append(room.title)
            data.append(room.population)

        await self.send(text_data=json.dumps(
        {
            'labels': labels,
            'data': data,

        })) 

signal.py * Пока что 1015 * from django.db.models.signals import post_save from django.dispatch import receiver from channels.layers import get_channel_layer from asgiref.sync import async_to_sync from .models import Room @receiver(post_save, sender=Room) def room_save_handler(sender, instance, **kwargs): channel_layer = get_channel_layer() async_to_sync(channel_layer.group_send)( 'chat_room_dash', { 'type': 'send_message', 'update': instance.population, } )

При чтении ошибки я попытался предложить предлагаемое решение, изменивsignal.py room_save_handler (), чтобы быть асинхронным и ожидать сообщения в группе.

Однако это никогда не отправляло сообщение, когда я обновлял модель вручную или когда пользователь заходил в чат.

Мой гость в том, что когда первый потребитель вызывает функцию room.save (), также вызывается функция room_save_handler (), что означает, что внутри асинхронного вызова есть асинхронный вызов.

Любая помощь будет отличной!

1 Ответ

0 голосов
/ 27 февраля 2019

У меня та же проблема, и я решил ее следующим способом :

import asyncio

from django.db.models.signals import post_save
from django.dispatch import receiver
from channels.layers import get_channel_layer


from .models import Room


@receiver(post_save, sender=Room)
def room_save_handler(sender, instance, **kwargs):
    channel_layer = get_channel_layer()
    loop = asyncio.get_event_loop()

    coroutine = async_to_sync(channel_layer.group_send)(
        'chat_room_dash',
        {
            'type': 'send_message',
            'update': instance.population,
        }
    )
    loop.run_until_complete(coroutine)
...