Django channels: Save messages to database
Asked Answered
L

2

8

I'm a newbie to channels and I made a chatroom application by following their official documentation. Now I'm trying to save the chat messages. All I know is I can create a model but Idk how to save it from the consumers.py into my database. I added username along with the message. A little help would be appreciated.

My Consumers.py:

import json
from channels.generic.websocket import AsyncWebsocketConsumer

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

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

        await self.accept()

    async def disconnect(self, close_code):
        # 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):
        username = self.scope["user"].first_name
        name = self.scope['user'].username
        text_data_json = json.loads(text_data)
        message = text_data_json['message']
        message = (username + '(' + name + ')' + ':\n' + 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):
        username = self.scope["user"].username
        message = event['message']
        name = self.scope["user"].username

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

My model to save the msgs:

class Message(models.Model):
    author = models.ForeignKey(User, related_name='messages', on_delete=models.CASCADE)
    context = models.TextField()
    timestamp = models.DateTimeField(auto_now_add=True)

I tried to follow this tutorial as well: https://www.youtube.com/watch?v=xrKKRRC518Y

Lanita answered 3/10, 2020 at 20:56 Comment(1)
Did you manage to solve it? Can you help me with the solution you found?Riggall
H
12

For the users who still have this issue:

Since the WebSocket being used is AsyncWebsocketConsumer, we have to use database_sync_to_async. Why so? Because we do not want to leave open connections to the database.

This is how you will do it:

@database_sync_to_async
def create_chat(self, msg, sender):
    Message.objects.create(sender=sender, msg=msg)

This is a simple example to demonstrate how to successfully use database_sync_to_async:

import json

from channels.db import database_sync_to_async
from channels.generic.websocket import AsyncWebsocketConsumer

from .models import Message


class ChatConsumer(AsyncWebsocketConsumer):
    @database_sync_to_async
    def create_chat(self, msg, sender):
        return Message.objects.create(sender=sender, msg=msg)

    async def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = 'chat_%s' % self.room_name
        await self.channel_layer.group_add(self.room_group_name, self.channel_name)
        await self.accept()

    async def disconnect(self, close_code):
        await self.channel_layer.group_discard(self.room_group_name, self.channel_name)

    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']
        sender = text_data_json['sender']
        await self.channel_layer.group_send(self.room_group_name, {
            'type': 'chat_message',
            'message': message,
            'sender': sender
        })

    async def chat_message(self, event):
        message = event['message']
        sender = event['sender']
        new_msg = await self.create_chat(sender, message)  # It is necessary to await creation of messages
        await self.send(text_data=json.dumps({
            'message': new_msg.message,
            'sender': new_msg.sender
        }))
Hymnody answered 19/8, 2021 at 15:11 Comment(0)
T
1

What's wrong with just creating the message regularly?

message = Message.objects.create(context=message, author=self.scope['user'])
Trichoid answered 3/10, 2020 at 21:20 Comment(4)
Oh I didn't try that. I believe I should place it in the receive function right?Lanita
I think so, yeah, not super experienced with channels but depending on where you do this, there is a possibility that you get duplicate messages. So you need to try it out.Kalil
But this would give an error as it cannot be used in async contextRiggall
did you find out how to do this? I have excactly the same problem :/Autoerotic

© 2022 - 2024 — McMap. All rights reserved.