How to integrate channels and DRF together

拥有回忆 提交于 2019-12-11 01:48:17

问题


I'm currently trying to create a backend server to communicate with some clients with a websocket. The clients makes some request to the backend and the backend responds directly to the client through a consumer.

In addition, I've got an API that needs to send some requests to the client. It has to go through the opened socket of the consumer. I'm using Django Rest Framework for the API. So I've got 2 apps for now. One for the consumer and one for the API. I want to know if it's the correct way or not.

This is actually the code I'm thinking about right now:

# mybackendapp/consumers.py

class MyConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.client_id = self.scope['url_route']['kwargs']['client_id']
        # This line I don't get it very well. It comes from:
        # [channels doc: single channels][1]
        # I don't know if I should create the Clients myself or if it's 
        # created automatically
        Clients.objects.create(channel_name=self.channel_name, 
                               self.client_id)
        self.accept()

    async def disconnect(self):
         Clients.objects.filter(channel_name=self.channel_name).delete()

    async def receive(self, text_data):
        self.recv_data = json.loads(text_data)
        if self.recv_data[0] == CLIENT_REQUEST:
            self.handler = ClientRequestHandler(self.client_id, 
                                                self.recv_data)
            await self.handler.run()
            self.sent_data = self.handler.response
            self.send(self.sent_data)
        elif self.recv_data[0] == CLIENT_RESPONSE:
            self.handler = ClientResponseHandler(self.client_id,
                                                 self.recv_data)
            channel_layer = get_channel_layer()
            # Here I'm not sure but I may have several API requests so
            # several row with the same client_id.
            # I welcome info on how to deal with that.
            api_channel_name = self.another_handler.ext_channel_name
            channel_layer.send(api_channel_name, {
                "text_data": self.handler.response,
            })

    async def message_from_api(self, event):
        self.api_channel_name = event['channel_name_answer']
        # this line is for hiding the fact that I'm only manipulating data
        # to send it to a correct format to the socket
        self.another_handler = ExternalDataHandler(event['json_data'])
        query_to_client = another_handler.get_formatted_query()
        self.send(query_to_client)

In receive, this consumer handles differently the messages from the client depending if it's initiated by the client or the rest API. You can see that with CLIENT_REQUEST and CLIENT_RESPONSE constants. Now from the API view:

# myapi/views.py

from channels.layers import get_channel_layer

def my_api_view(request, client_id):
    channel_layer = get_channel_layer()
    if request.method == 'POST':
        ext_request_data_json = request.data
        client_channel_name = Clients.objects.get(
            client_id=client_id).channel_name
        # I don't know what type is listen_channel_name. I assume it's str
        listen_channel_name = async_to_sync(channels_layers.new_channel)()
        async_to_sync(channel_layer.send)(
            client_channel_name, {
                'type': 'message.from.api',
                'json_data': ext_request_data_json,
                'channel_name_answer': listen_channel_name
            }
        )
        received_msg = channel_layer.receive(listen_channel_name)

I believe that this code should work. I want to know if it's the correct way to do it.

来源:https://stackoverflow.com/questions/54350826/how-to-integrate-channels-and-drf-together

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!