web-dev-qa-db-fra.com

Envoi de messages à des groupes dans Django Channels 2

Je suis complètement coincé dans l'impossibilité de faire fonctionner la messagerie de groupe avec Channels 2! J'ai suivi tous les tutoriels et documents que je pouvais trouver, mais hélas, je n'ai pas encore trouvé le problème. Ce que j'essaie de faire actuellement, c'est d'avoir une URL spécifique qui, lorsqu'elle est visitée, devrait diffuser un simple message à un groupe nommé "événements".

Tout d’abord, voici les paramètres pertinents et actuels que j’utilise dans Django:

CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            'hosts': [('localhost', 6379)],
        },
    }
}

ASGI_APPLICATION = 'backend.routing.application'

INSTALLED_APPS = [
    'Django.contrib.admin',
    'Django.contrib.auth',
    'Django.contrib.contenttypes',
    'Django.contrib.sessions',
    'Django.contrib.messages',
    'Django.contrib.staticfiles',
    'rest_framework',
    'corsheaders',
    'channels',
    'channels_redis',
    'backend.api'
]

Ensuite, voici mon EventConsumer, étendant le JsonWebsocketConsumer de manière très basique. Tout cela ne fait que retentir lorsque vous recevez un message, ce qui fonctionne! Ainsi, la simple réponse send_json arrive comme il se doit, c’est UNIQUEMENT la diffusion de groupe qui ne fonctionne pas.

class EventConsumer(JsonWebsocketConsumer):
    groups = ["events"]

    def connect(self):
        self.accept()

    def disconnect(self, close_code):
        print("Closed websocket with code: ", close_code)
        self.close()

    def receive_json(self, content, **kwargs):
        print("Received event: {}\nFrom: {}\nGroups: 
                               {}".format(content, 
                                          self.channel_layer, 
                                          self.groups))

        self.send_json(content)

    def event_notification(self, event):
        self.send_json(
            {
                'type': 'test',
                'content': event
            }
        )

Et voici les configurations d'URL pour l'URL que je veux déclencher la diffusion:

Projet urls.py

from backend.events import urls as event_urls

urlpatterns = [
    url(r'^events/', include(event_urls))
]

App événements urls.py

from backend.events.views import alarm

urlpatterns = [
    url(r'alarm', alarm)
]

Et enfin, la vue elle-même où la diffusion du groupe devrait avoir lieu:

from Django.shortcuts import HttpResponse
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync


def alarm(req):
    layer = get_channel_layer()
    async_to_sync(layer.group_send)('events', {'type': 'test'})
    return HttpResponse('<p>Done</p>')
6
Måns Thörnvik

J'ai trouvé la solution en écrivant ces questions et j'ai pensé que quelqu'un d'autre pourrait aussi s'en servir! Comme la plupart des questions ici concernent la version des chaînes antérieure à 2.0 et ultérieure, voici comment gérer les événements group_send chez vos consommateurs. 

Le problème ne résidait pas seulement dans la façon dont j'ai utilisé la fonction group_send, cependant, j'avais supposé à tort que l'ajout de la variable de classe groups à mon EventConsumer devrait l'ajouter automatiquement à celui/à ces groupes, ce n'est PAS! Vous devez ajouter des groupes manuellement dans la fonction de classe connect et supprimer des groupes dans la fonction disconnect!

Le problème a ensuite menti en ce sens que mon consommateur ne disposait pas des gestionnaires d’événements appropriés. Dans mon fichier de vue, où la demande d'alarme est prise en charge, j'avais défini le "type" sur "test". Le test n'a pas été reflété dans ma classe EventConsumer afin que l'événement ne puisse pas être traité. Comme indiqué dans l'exemple multichat ici sur le numéro de ligne 146, les fonctions d'assistance sont appelées en fonction du type d'événement envoyé. Donc, un type d'événement de 'event.alarm' devrait avoir une fonction correspondante de event_alarm dans votre consommateur! Simple, mais pas si bien documenté :). Voici à quoi ressemblait la solution finale:

Dans consumers.py, notez le group_add dans connect et le group_discard dans déconnectez!

class EventConsumer(JsonWebsocketConsumer):

    def connect(self):
        async_to_sync(self.channel_layer.group_add)(
            'events',
            self.channel_name
        )
        self.accept()

    def disconnect(self, close_code):
        print("Closed websocket with code: ", close_code)
        async_to_sync(self.channel_layer.group_discard)(
            'events',
            self.channel_name
        )
        self.close()

    def receive_json(self, content, **kwargs):
        print("Received event: {}".format(content))
        self.send_json(content)

    # ------------------------------------------------------------------------------------------------------------------
    # Handler definitions! handlers will accept their corresponding message types. A message with type event.alarm
    # has to have a function event_alarm
    # ------------------------------------------------------------------------------------------------------------------

    def events_alarm(self, event):
        self.send_json(
            {
                'type': 'events.alarm',
                'content': event['content']
            }
        )

Donc, la fonction ci-dessus events_alarm est appelée à partir du group_send suivant:

from Django.shortcuts import HttpResponse

from channels.layers import get_channel_layer

from asgiref.sync import async_to_sync


def alarm(req):
    layer = get_channel_layer()
    async_to_sync(layer.group_send)('events', {
        'type': 'events.alarm',
        'content': 'triggered'
    })
    return HttpResponse('<p>Done</p>')

S'il vous plaît laissez-moi savoir si vous avez besoin de plus de précisions à la question/réponse! À votre santé!

15
Måns Thörnvik

J'ai eu un problème similaire à celui-ci pendant un certain temps également, bien que la raison pour laquelle group_send ne fonctionnait pas était qu'un websocket n'était pas réellement connecté. 

Lors du test du serveur de développement rechargé qui a déconnecté le socket, les appels suivants ne sont pas exécutés par le consommateur. L'actualisation de l'interface a reconnecté le socket et group_send a commencé à fonctionner.

Bien que cela ne réponde pas directement à la question, j'espère que cela pourrait aider quelqu'un.

1
Richard Pryce