Creating/Editing (updating) messages with Django-Channels

Hello guys, how do I create/update the messages that end-users will send to the other users in my consumers? Would I have to implement something like self.send() in the receive method?

Here is my consumers.py

import json
from channels.consumer import AsyncConsumer

from channels.generic.websocket import AsyncJsonWebsocketConsumer
from asgiref.sync import sync_to_async
import base64


# Channel consumer for both chat room group

class ChatConsumer(AsyncJsonWebsocketConsumer):
    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):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']

        if message_type == 'edit_message':
            message_id = text_data_json['message_id']
            new_message = text_data_json['new_message']
            await self.edit_message(message_id, new_message)
            

        @sync_to_async
        def edit_message(self, message_id, new_message):
            # update retreived message from database
            message = Message.objects.get(id=message_id)
            message.content = new_message
            message.save()

        # broadcast (send) to group
        self.channel_layer.group_send(
            self.group_name,
            {
                'type': 'message_edited',
                'message_id': message_id,
                'new_message': new_message,
            }
        }

    async def message_edited(self, event):
        message_id = event['message_id']
        new_message = event['new_message']
        await self.send(text_data=json.dumps({
            'type': 'message_edited',
            'message_id': message_id,
            'new_message': new_message,
        }))
        print(f"Received message: {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']
        print(f"Broadcasting message: {message}")  # Debugging output
        # send message to websocket
        await self.send(text_data=json.dumps({'message': message}))
        
        
# Channel Consumer that consumed events for uploading/receiving image files in the message room

class UploadImageConsumer(AsyncJsonWebsocketConsumer):
    async def connect(self):
        await self.accept()
        

    async def disconnect(self, close_code):

        async def receive(self, bytes_data=None, text_data=None):
            if bytes_data:
            # Handles image file chunks (binary data)
            
                with open('image.jpg', 'rb') as f:
                    fcontent = f.read()
                await self.send(base64.b64encode(fcontent).decode('utf-8'))
            elif text_data:
                # Handles metadata (text data)
            
                await self.send(text_data=json.dumps({'message': text_data}))
            
            
    async def send_image(self, event):
        image_data = event['image_text']
        await self.send(bytes_data=image_data)

messenger.html

{% load static %}

<!DOCTYPE html>



<html lang="en">
    <head>
    <title>ArborHub messenger</title>
        <link rel="icon" href="{% static 'favicon.ico' %}">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js"></script>

    <style>
        #messageLog {
            height: 300px;
            background-color: #FFFFFF;
            resize: none;
        }

        #onlineArboristSelector {
            height: 300px;
        }
    </style>
</head>
<body>

    <textarea id='chat-text' cols='80' rows='30'></textarea>

    <div id='hello-world'></div>

    <div class="container mt-3 p-5">
        <h2>ArborHub messenger</h2>
        <div class="row">
            <div class="col-12 col-md-8">
                <div class="mb-2">
                    <label for="messageLog">Room: #{{ room.name}}</label>
                    <textarea class="form-control" id="messageLog" readOnly></textarea>
                </div>
                <div class="input-group">
                    <input type="text" class="form-control" id="channelMessage" placeholder="Message here">
                    <div class="input-group-append">
                        <button class="btn btn-success" id="sendMessage" type="button">Send</button>
                    </div>
                </div>
            </div>
            <div class="col-12 col-md-4">
               <label for="onlineArboristSelector">Online arborists</label>
                <select multiple class="form-control" id="onlineArboristSelector">
                </select>
            </div>
        </div>
    </div>
{{ room_name|json_script:'room-name'}}
    <script>
        document.querySelector('#sendMessage').onclick = function (e) {
            const messageInputDom = document.querySelector('#channelMessage');
            const message = messageInputDom.value;
            chatSocket.send(JSON.stringify({
                'message': message,
            }));

            function editMessage(messageId, newMessage) {
                websocket.send(JSON.stringify({
                    'type': 'edit_message',
                    'message_id': messageId,
                    'new_message': new Message
                }));
            }

            websocket.onmessaage = function(event) {
                const data = JSON.parse(event.data);
                if (dataa.type === 'message_edited') {
                    const messageId = data.message_id;
                    const newMessage = data.new_message;

                }
            }
            messageInputDom.value = '';
        };
        const roomName = JSON.parse(document.getElementById('room-name').textContent);

        const chatSocket = new WebSocket(
            'ws://' +
            location.host +
            '/ws/arborchat/' +
            roomName +
            '/'
        );

        chatSocket.onmessage = function (e) {
            const data = JSON.parse(e.data);
            console.log("Received message:", data);
            const messageLog = document.querySelector('#messageLog');
            messageLog.value += data.message + '\n';
        }
    </script>
</body>

</html>

Someone suggested that the code doesn’t specify when the message ID was created in consumers. Please I need guidance on this.

Even a link would help

I’m not sure I understand what you’re asking here.

An “end user” doesn’t create messages in a consumer. The consumers are Python code running in the server. An end user would be running JavaScript in the browser.

So, if you look at Part 2 of the Channels Tutorial, you’ll see the JavaScript code that sends the messages through the websocket to its consumer.

(If you haven’t already worked your way through the Channels Tutorial, you should. It covers 90+% of everything you need to know to use it.)

I don’t know why I thought you could configure an edit message logic via consumers.py. Someone on the Django discord channel was telling to update the message both in the consumers/template (JavaScript).

And yes, I’ve gone through the tutorial. But I can always get a refresher

You can - a consumer can create and/or modify messages. However, then it’s the consumer doing that, and not the end user.

When an end user sends a message through the websocket to the consumer, that user’s consumer receives the message in the WebsocketConsumer.receive method. That method can then do whatever you want it to do to that message.

As far as having the consumer create messages, “something” needs to trigger that to occur - that event (whatever it needs to be) would need to be handled by that consumer to generate the message.

That’s what I was thinking. So how do I create that event that edits messages via consumers then?

Sorry for the confusion, I didn’t mean end-user in the sense of consumers. Just the logic that hands creating/editing messages in consumers

Again, I don’t think I’m following you. It might be helpful if you were more specific about what specifically what the requirement is.

When the browser sends a message to the consumer, the consumer receives the message via the WebsocketConsumer.receive method. If that’s the message that you want to modify, the “event” is the fact that the receive method has been called with the message that was sent. You can do anything you want to do to the message in that method.

Yes, I would like to create a method that updates the message sent from the browser (request) to the consumer, via WebsocketConsumer.receive method.

I apologize for the confusion. I want to edit the message in the receive method

Does this make more sense now?

Even if you could send me a link to a tutorial/article, that would be great

The tutorial shows how to access the message in the receive method.

It seems like I already implemented the logic to send the messages via websockets to the consumers

That would be JavaScript code running in the browser, yes.

Yeah I just wanted to see how to incorporate the function to edit messages in consumers after they were created.

I’ll just back to the docs then

That’s addressed in two previous replies

and

I did go through the tutorial already.

I apologize, but this wasn’t exactly helpful. I was as specific as I could be in my post. It is fine, I will figure this out myself.

I want to give the end-user the option to edit an already-sent message from typos and such. Maybe this is a frontend issue

It’s more than that.

Messages sent via a basic chat system have no persistance on the server. They are received by the consumer, and immediately forwarded to all intended recipients. The consumers don’t keep the messages.

So doing this involves somehow tracking the messages to allow the original sender the option to select and edit it, then resending it, then having all the recipients figure out that the resent message should replace the original in their chat window. (Otherwise, the recipients would just see this as a “corrected copy” of the original.)

Logically, what you’re doing here isn’t “editing” a message, you would effectively be sending a replacement message. And yes, the majority of work involved with this is going to be in the front ends. This is likely going to be done in JavaScript, with no need to change your consumer at all.

Or, there is another approach which is something that I do.
I use HTMX for their websocket support. You could have the consumer “wrap” the message in an HTML span element with a unique id, then allow HTMX to inject that element into your page. If the id happens to match an existing id on the page, HTMX will replace that element.

Well I’m using Vuejs, not standard Django templates as the frontend