I’m fairly new to Django / Channels so apologies if everything required isn’t being shown here. I’d be happy to provide further info if necessary.
Anyway, I’ve been working on a one-to-one chat application and am running into this error:
ValueError: Cannot assign "'admin'": "Message.sender" must be a "UserProfileModel" instance.
This issue is that I thought admin
was an instance of my user model? admin
is the username of my superuser, which is logged in as I get this error. When I try to change the admin
input to self.scope['user]
I then get a serialization error. Any help is appreciated.
Here is my consumers.py file:
import json
from channels.generic.websocket import WebsocketConsumer
from django.conf import settings (unused)
from accounts.models import UserProfileModel
from asgiref.sync import async_to_sync
from chat.models import Thread, Message
class ChatConsumer(WebsocketConsumer):
def create_chat(self, msg, sender):
new_msg = Message.objects.create(sender=sender, text=msg)
new_msg.save()
return new_msg
def connect(self):
self.me = self.scope['user']
self.user_name = self.scope['url_route']['kwargs']['room_name']
self.you = UserProfileModel.objects.get(username=self.user_name)
self.thread_obj = Thread.objects.get_or_create_personal_thread(self.me, self.you)
self.room_group_name = f'personal_thread_{self.thread_obj.id}'
# Join room group
async_to_sync(self.channel_layer.group_add)(
self.room_group_name,
self.channel_name
)
self.accept()
print(f'[{self.channel_name}] [{self.room_group_name}] connected')
def disconnect(self, close_code):
# Leave room group
async_to_sync(self.channel_layer.group_discard)(
self.room_group_name,
self.channel_name
)
# Receive message from WebSocket
def receive(self, text_data):
text_data_json = json.loads(text_data) # WHAT IS TEXT_DATA REFERRING TO?
message = text_data_json['message']
# DOES A SENDER NEED TO GO INSIDE TEXT_DATA?
# Send message to room group
async_to_sync(self.channel_layer.group_send)(
self.room_group_name,
{
'type': 'chat_message',
'message': message,
'sender' : self.scope['user'].username #THIS SEEMS TO BE THE ROOT OF THE ISSUE | DOES THE SENDER FROM JSON NEED TO GO HERE
}
)
# Receive message from room group
def chat_message(self, event):
message = event['message']
sender = event['sender']
new_msg = self.create_chat(msg=message, sender=sender)
# Send message to WebSocket
self.send(text_data=json.dumps({
'message': new_msg.msg,
'sender' : new_msg.sender
}))
models.py
from django.conf import settings
from chat.managers import ThreadManager
from django.db import models
class TrackingModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Thread(TrackingModel):
THREAD_TYPE = (
('personal', 'Personal'),
('group', 'Group')
)
name = models.CharField(max_length=50, null=True, blank=True)
thread_type = models.CharField(max_length=15, choices=THREAD_TYPE, default='group')
users = models.ManyToManyField(settings.AUTH_USER_MODEL)
objects = ThreadManager()
def __str__(self) -> str:
if self.thread_type == 'personal' and self.users.count() == 2:
return f'{self.users.first()}' and f'{self.users.last()}'
return f'{self.name}'
class Message(TrackingModel):
thread = models.ForeignKey(Thread, on_delete=models.CASCADE)
sender = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
text = models.TextField(blank=False, null=False)
def __str__(self) -> str:
return f'From <Thread - {self.thread}>'
managers.py
from django.db import models
from django.db.models import Count
class ThreadManager(models.Manager):
def get_or_create_personal_thread(self, user1, user2):
threads = self.get_queryset().filter(thread_type='personal')
threads = threads.filter(users__in=[user1, user2]).distinct()
threads = threads.annotate(u_count=Count('users')).filter(u_count=2)
if threads.exists():
return threads.first()
else:
thread = self.create(thread_type='personal')
thread.users.add(user1)
thread.users.add(user2)
return thread
def by_user(self, user):
return self.get_queryset().filter(users__in=[user])
views.py
from django.shortcuts import render
# Create your views here.
def index(request): #Change to IndexView
return render(request, 'chat/chatindex.html')
def room(request, room_name): #Change to RoomView
return render(request, 'chat/chatroom.html', {
'room_name': room_name
})