RZAMarefat
RZAMarefat

Reputation: 143

TypeError: can not serialize 'User' object in Chat Application

I want to integrate a chat app with my instagram-like project. My primary goal is to provide the users of this website with the possibility to chat with each other real-time. I have the following code but I keep getting the error:

TypeError: can not serialize 'User' object

from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
import json
from .models import Message
from django.contrib.auth.models import User

class ChatConsumer(WebsocketConsumer):
    def fetch_messages(self, data):
        messages = Message.last_10_messages()
        content = {
            'messages': self.messages_to_json(messages)
        }
        self.send_message(content)
       
    def new_message(self, data):
        author = data['from']
        author_user = User.objects.get(username = author)
        message = Message.objects.create(author=author_user, content=data['message'])
        content ={
            'command' : 'new_message',
            'message': self.message_to_json(message)
        }
        return self.send_chat_message(content) 
    
    def messages_to_json(self, messages):
        result = []
        for message in messages:
            result.append(self.message_to_json(message))
        return result
    
    def message_to_json(self, message):
        return {
            'author' : message.author,
            'content' : message.content,
            'timestamp': str(message.timestamp)
        }


    commands = {
        'fetch_messages': fetch_messages,
        'new_message' : new_message
    }

   
   
    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
        async_to_sync(self.channel_layer.group_add)(
            self.room_group_name,
            self.channel_name
        )

        self.accept()

    def disconnect(self, close_code):
        # Leave room group
        async_to_sync(self.channel_layer.group_discard)(
            self.room_group_name,
            self.channel_name
        )

    
    def receive(self, text_data):
        data = json.loads(text_data)
        self.commands[data['command']](self, data)
        
    def send_chat_message(self, data):    
        message = data['message']
        async_to_sync(self.channel_layer.group_send)(
            self.room_group_name,
            {
                'type': 'chat_message',
                'message': message
            }
        )

    def send_message(self, message):
        self.send(text_data=json.dumps(message))

    def chat_message(self, event):
        message = event['message']

        self.send(text_data=json.dumps(message))

The views.py:

from django.shortcuts import render
from django.utils.safestring import mark_safe
import json


def index(request):
    return render(request, 'chat/index.html', {})

def room(request, room_name):
    return render(request, 'chat/room.html', {
        'room_name_json': mark_safe(json.dumps(room_name)),
        'username' : mark_safe(json.dumps(request.user.username))
    })

The routing.py

from django.urls import re_path, path

from . import consumers

websocket_urlpatterns = [
    re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
]

The asgi.py in the project root:

import os

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
import chat.routing

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")

application = ProtocolTypeRouter({
  "http": get_asgi_application(),
  "websocket": AuthMiddlewareStack(
        URLRouter(
            chat.routing.websocket_urlpatterns
        )
    ),
})

I am following the tutorial on the official website of Django Channels. I am also trying to customise my consumer to save it on database and the model for that is as following:

from django.db import models from django.contrib.auth.models import User

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

def __str__(self):
    return self.author.username

def last_10_messages(self):
    return Message.objects.order_by('-timestamp').all()[:10]

Upvotes: 0

Views: 754

Answers (2)

iiMouad
iiMouad

Reputation: 35

replace message.author with message.author.username

Upvotes: 0

MrObjectOriented
MrObjectOriented

Reputation: 282

self.channel_layer.group_send requires the message dict to contain the following values only:

  1. Byte strings
  2. Unicode strings
  3. Integers (within the signed 64 bit range)
  4. Floating point numbers (within the IEEE 754 double precision range)
  5. Lists (tuples should be encoded as lists)
  6. Dicts (keys must be unicode strings)
  7. Booleans
  8. None

Source: https://channels.readthedocs.io/en/latest/channel_layer_spec.html#messages

Upvotes: 0

Related Questions