Cannot assign "'username'": "Message.sender" must be a "User" instance

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
    })

What is the full traceback you are receiving?

Are you using a custom User model or the system standard default model?

What does your UserProfileModel look like?

In your connect function, you have:
self.me = self.scope['user']
self.you = UserProfileModel.objects.get(username=self.user_name)

This is going to be two different types of objects. self.me is of type User, while self.you is a UserProfileModel.

Since:

is expecting two objects of the same type (your User model), this will fail.

(Also, your indentation for your code is messed up here - it’s really difficult to read.)

The full traceback:

System check identified no issues (0 silenced).
April 23, 2022 - 22:47:35
Django version 4.0.4, using settings 'portfoliosite.settings'
Starting ASGI/Channels version 3.0.4 development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
HTTP GET / 200 [0.16, 127.0.0.1:54553]
HTTP GET /chat/ 200 [0.01, 127.0.0.1:54553]
HTTP GET /chat/admin/ 200 [0.01, 127.0.0.1:54553]
WebSocket HANDSHAKING /ws/chat/admin/ [127.0.0.1:54558]
WebSocket CONNECT /ws/chat/admin/ [127.0.0.1:54558]
[specific.060732f0e2c941aba746f5a8b0e6b6c3!0a2b8f1bcafe41b485af1a05c34516f5] [personal_thread_67] connected
Exception inside application: Cannot assign "'admin'": "Message.sender" must be a "UserProfileModel" instance.
Traceback (most recent call last):
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\staticfiles.py", line 44, in __call__
    return await self.application(scope, receive, send)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\routing.py", line 71, in __call__
    return await application(scope, receive, send)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\security\websocket.py", line 37, in __call__
    return await self.application(scope, receive, send)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\sessions.py", line 47, in __call__
    return await self.inner(dict(scope, cookies=cookies), receive, send)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\sessions.py", line 263, in __call__
    return await self.inner(wrapper.scope, receive, wrapper.send)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\auth.py", line 185, in __call__
    return await super().__call__(scope, receive, send)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\middleware.py", line 26, in __call__
    return await self.inner(scope, receive, send)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\routing.py", line 150, in __call__
    return await application(
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\consumer.py", line 94, in app
    return await consumer(scope, receive, send)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\consumer.py", line 58, in __call__
    await await_many_dispatch(
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\utils.py", line 51, in await_many_dispatch
    await dispatch(result)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\asgiref\sync.py", line 414, in __call__
    ret = await asyncio.wait_for(future, timeout=None)
  File "C:\Users\metin\AppData\Local\Programs\Python\Python39\lib\asyncio\tasks.py", line 442, in wait_for
    return await fut
  File "C:\Users\metin\AppData\Local\Programs\Python\Python39\lib\concurrent\futures\thread.py", line 52, in run
    result = self.fn(*self.args, **self.kwargs)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\db.py", line 13, in thread_handler
    return super().thread_handler(loop, *args, **kwargs)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\asgiref\sync.py", line 455, in thread_handler
    return func(*args, **kwargs)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\consumer.py", line 125, in dispatch
    handler(message)
  File "C:\coding\python\portfoliosite\chat\consumers.py", line 72, in chat_message
    new_msg = self.create_chat(msg=message, sender=sender) # new
  File "C:\coding\python\portfoliosite\chat\consumers.py", line 14, in create_chat
    new_msg = Message.objects.create(sender=sender, text=msg)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\django\db\models\manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\django\db\models\query.py", line 512, in create
    obj = self.model(**kwargs)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\django\db\models\base.py", line 541, in __init__
    _setattr(self, field.name, rel_obj)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\django\db\models\fields\related_descriptors.py", line 235, in __set__
    raise ValueError(
ValueError: Cannot assign "'admin'": "Message.sender" must be a "UserProfileModel" instance.

Ken, in my project’s settings.py file I set the AUTH_USER_MODEL to UserProfileModel. Maybe this could be a routing issue? Please let me know if you need those files. Thanks.

Here is my UserProfileModel:

class UserProfileModel(AbstractUser):
    pass
    bio = models.TextField() # Additional field
    city = models.TextField() # Additional field

    ADMIN = 0 # new
    EMPLOYER = 1 # new
    EMPLOYEE = 2 # new

    USER_LEVEL_CHOICES = ((ADMIN, "Admin"), (EMPLOYER, "Employer"), (EMPLOYEE, "Employee")) # new

    status = models.IntegerField(choices=USER_LEVEL_CHOICES, blank=True, default=EMPLOYEE)

No, this isn’t a routing issue. It’s a data type issue. I’m not in a position to verify anything at the moment, but somewhere in this code you’re passing a string ‘admin’ instead of the object. It’s just an issue of finding where that’s happening.

Okay. I believe the issue is coming from the consumers.py file in the ‘receive’ method specifically. As far as I know, this is where ‘admin’ is coming from. However when I change self.scope['user].username to simply self.scope['user'] I get a serialization error. That error is below

System check identified no issues (0 silenced).
April 24, 2022 - 00:05:02
Django version 4.0.4, using settings 'portfoliosite.settings'
Starting ASGI/Channels version 3.0.4 development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
C:\coding\python\portfoliosite\chat\consumers.py changed, reloading.
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
April 24, 2022 - 00:22:06
Django version 4.0.4, using settings 'portfoliosite.settings'
Starting ASGI/Channels version 3.0.4 development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
HTTP GET / 200 [0.03, 127.0.0.1:55982]
HTTP GET / 200 [0.02, 127.0.0.1:55982]
HTTP GET /chat/ 200 [0.02, 127.0.0.1:55982]
HTTP GET /chat/admin/ 200 [0.01, 127.0.0.1:55982]
WebSocket HANDSHAKING /ws/chat/admin/ [127.0.0.1:55994]
WebSocket CONNECT /ws/chat/admin/ [127.0.0.1:55994]
[specific.a5627b87b7f64233af8d1f24911baff2!81f2b04225534095b81427f694feff39] [personal_thread_68] connected
Exception inside application: can not serialize 'UserLazyObject' object
Traceback (most recent call last):
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\staticfiles.py", line 44, in __call__
    return await self.application(scope, receive, send)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\routing.py", line 71, in __call__
    return await application(scope, receive, send)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\security\websocket.py", line 37, in __call__
    return await self.application(scope, receive, send)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\sessions.py", line 47, in __call__
    return await self.inner(dict(scope, cookies=cookies), receive, send)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\sessions.py", line 263, in __call__
    return await self.inner(wrapper.scope, receive, wrapper.send)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\auth.py", line 185, in __call__
    return await super().__call__(scope, receive, send)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\middleware.py", line 26, in __call__
    return await self.inner(scope, receive, send)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\routing.py", line 150, in __call__
    return await application(
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\consumer.py", line 94, in app
    return await consumer(scope, receive, send)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\consumer.py", line 58, in __call__
    await await_many_dispatch(
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\utils.py", line 51, in await_many_dispatch
    await dispatch(result)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\asgiref\sync.py", line 414, in __call__
    ret = await asyncio.wait_for(future, timeout=None)
  File "C:\Users\metin\AppData\Local\Programs\Python\Python39\lib\asyncio\tasks.py", line 442, in wait_for
    return await fut
  File "C:\Users\metin\AppData\Local\Programs\Python\Python39\lib\concurrent\futures\thread.py", line 52, in run
    result = self.fn(*self.args, **self.kwargs)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\db.py", line 13, in thread_handler
    return super().thread_handler(loop, *args, **kwargs)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\asgiref\sync.py", line 455, in thread_handler
    return func(*args, **kwargs)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\consumer.py", line 125, in dispatch
    handler(message)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels\generic\websocket.py", line 59, in websocket_receive
    self.receive(text_data=message["text"])
  File "C:\coding\python\portfoliosite\chat\consumers.py", line 56, in receive
    async_to_sync(self.channel_layer.group_send)(
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\asgiref\sync.py", line 204, in __call__
    return call_result.result()
  File "C:\Users\metin\AppData\Local\Programs\Python\Python39\lib\concurrent\futures\_base.py", line 438, in result
    return self.__get_result()
  File "C:\Users\metin\AppData\Local\Programs\Python\Python39\lib\concurrent\futures\_base.py", line 390, in __get_result
    raise self._exception
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\asgiref\sync.py", line 270, in main_wrap
    result = await self.awaitable(*args, **kwargs)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels_redis\core.py", line 685, in group_send
    ) = self._map_channel_keys_to_connection(channel_names, message)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels_redis\core.py", line 820, in _map_channel_keys_to_connection
    channel_key_to_message[key] = self.serialize(value)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\channels_redis\core.py", line 840, in serialize
    value = msgpack.packb(message, use_bin_type=True)
  File "C:\coding\python\portfoliosite\.venv\lib\site-packages\msgpack\__init__.py", line 35, in packb
    return Packer(**kwargs).pack(o)
  File "msgpack\_packer.pyx", line 294, in msgpack._cmsgpack.Packer.pack
  File "msgpack\_packer.pyx", line 300, in msgpack._cmsgpack.Packer.pack
  File "msgpack\_packer.pyx", line 297, in msgpack._cmsgpack.Packer.pack
  File "msgpack\_packer.pyx", line 231, in msgpack._cmsgpack.Packer._pack
  File "msgpack\_packer.pyx", line 291, in msgpack._cmsgpack.Packer._pack
TypeError: can not serialize 'UserLazyObject' object

Are you trying to send a whole user object out as a message? If so, why?

No not at all. I just want the sender to be captured somehow.

Okay think I got an answer. Completely updated the consumers.py file. Aside from that change, nothing else changed except for adding the username in the html file for each message. The integrity of only allowing users from within my auth model works by sending an error if I enter an id of a user that doesn’t exist in the model. I think that solves my issue?

class ChatConsumer(WebsocketConsumer):
    def connect(self):
        self.sender = self.scope['user'].username
        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
        )

    # Receive message from WebSocket
    def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']
        sender = self.sender

        # Send message to room group
        async_to_sync(self.channel_layer.group_send)(
            self.room_group_name,
            {
                'type': 'chat_message',
                'message': message,
                'sender' : sender
            }
        )

    # Receive message from room group
    def chat_message(self, event):
        message = event['message']
        sender = event['sender']

        # Send message to WebSocket
        self.send(text_data=json.dumps({
            'message': message,
            'sender' : sender
        }))

Thank you, Ken, for the help! Guided me into reading the docs a little better to understand consumers and routing.