I am trying to finalize the notifications system in my Django Channels application.

When I send a message or a string of text through the websocket, websocket.recieve will generate a load of empty strings after the initial message is sent.

127.0.0.1:54917 - - [27/Apr/2019:19:04:05] "WSCONNECTING /messages/trilla" - -
connected {'type': 'websocket.connect'}
127.0.0.1:54917 - - [27/Apr/2019:19:04:05] "WSCONNECT /messages/trilla" - -
receive {'type': 'websocket.receive', 'text': '{"message":"hi there"}'}
websocket.receive
receive {'type': 'websocket.receive', 'text': '{"message":""}'}
websocket.receive
receive {'type': 'websocket.receive', 'text': '{"message":""}'}
websocket.receive
receive {'type': 'websocket.receive', 'text': '{"message":""}'}
websocket.receive

The navbar has a JS script which adds new notifications. An html string is constructed from the server message and then inserted to the page html.

I am getting NOT NULL constraint failed: chat_chatmessage.message when I click on the navbar notification icon. I assume because of the empty string problem generating a load of empty messages.

inbox problem

This is the log when I click on the notification icon

receive {'type': 'websocket.receive', 'text': '{"type":"notification_read","username":{},"notification_id":{"jQuery33100053785670652706231":{"display":""}}}'}
websocket.receive
2019-04-28 11:59:47,041 ERROR    Exception inside application: NOT NULL constraint failed: chat_chatmessage.message

It's clear the data is not getting passed properly. I followed a tutorial for the Django Channels portion and had no experience with JS / WebSockets before trying to do this so I'm limited in my skills to locate the problematic code.

consumers.py

async def websocket_receive(self, event):
        # when a message is recieved from the websocket
        print("receive", event)

        message_type = event.get('type', None)  #check message type, act accordingly
        print(message_type)
        if message_type == "notification_read":
            # Update the notification read status flag in Notification model.
            notification = Notification.object.get(id=notification_id)
            notification.notification_read = True
            notification.save()  #commit to DB
            print("notification read")

        front_text = event.get('text', None)
        if front_text is not None:
            loaded_dict_data = json.loads(front_text)
            msg =  loaded_dict_data.get('message')
            user = self.scope['user']
            username = 'default'
            notification_id = 'default'
            if user.is_authenticated:
                username = user.username
            myResponse = {
                'message': msg,
                'username': username,
                'notification': notification_id,
            }
            await self.create_chat_message(user, msg)

            # broadcasts the message event to be sent, the group send layer
            # triggers the chat_message function for all of the group (chat_room)
            await self.channel_layer.group_send(
                self.chat_room,
                {
                    'type': 'chat_message',
                    'text': json.dumps(myResponse)
                }
            )
    # chat_method is a custom method name that we made
    async def chat_message(self, event):
        # sends the actual message
        await self.send({
                'type': 'websocket.send',
                'text': event['text']
        })


 async def websocket_disconnect(self, event):
        # when the socket disconnects
        print('disconnected', event)

    @database_sync_to_async
    def get_thread(self, user, other_username):
        return Thread.objects.get_or_new(user, other_username)[0]

    @database_sync_to_async
    def create_chat_message(self, me, msg):
        thread_obj = self.thread_obj
        return ChatMessage.objects.create(thread=thread_obj, user=me, message=msg)

base.html

<script>
    $(document).ready(function() {
      $("#notificationLink").click(function() {
        var data = {
          "type": "notification_read",
          "username": username,
          "notification_id": notification_id,
        }
        socket.send(JSON.stringify(data));

        $("#notificationContainer").fadeToggle(300);
        $("#notification_id").fadeOut("slow");
        return false;
      });
</script>

<script>
    // websocket scripts - client side*
    var loc = window.location
    var formData = $("#form")
    var msgInput = $("#id_message")
    var chatHolder = $('#chat-items')
    var me = $('#myUsername').val()
    var notification = $("#notificationLink")

    var wsStart = 'ws://'
    if (loc.protocol == 'https:') {
      wsStart = 'wss://'
    }
    var endpoint = wsStart + loc.host + loc.pathname
    var socket = new ReconnectingWebSocket(endpoint)

    // below is the message I am receiving
    socket.onmessage = function(e) {
      var data = JSON.parse(event.data);
      // Find the notification icon/button/whatever and show a red dot, add the notification_id to element as id or data attribute.
      notification.append("<span id=#notification_id>" + notification.notification_id)

      console.log("message", e)
      var chatDataMsg = JSON.parse(e.data)
      chatHolder.append('<li>' + chatDataMsg.message + ' from ' + chatDataMsg.username + '</li>')
    }

    // below is the message I am sending
    socket.onopen = function(e) {
      console.log("open", e)
      formData.submit(function(event) {
        event.preventDefault()
        var msgText = msgInput.val()
        var finalData = {
          'message': msgText
        }
        socket.send(JSON.stringify(finalData))
        formData[0].reset()
      })
    }

navbar

<li id="notification_li" class="nav-item">
  <a class="nav-link" href="#" id="notificationLink">
    <i class="fas fa-envelope"></i>&nbsp; Inbox</a>
  {% for notifications in notification %}
  <span id="notification_id{{notification_id}}">{{ notifications.notification_chat }}</span>
  {% endfor %}
  <div id="notificationContainer">
    <div id="notificationTitle">Notifications</div>
    <div id="notificationsBody" class="notifications">
      {% for notifications in notification %}
      <a href="{% url 'chat:thread' user %}">
        <span id="notification-{{notification.id}}">
          {{ notifications.notification_chat.message }}
          via {{ notifications.notification_chat.user }}
          at {{ notifications.notification_chat.thread.timestamp }}
        </span>
      </a>
...

2 Answers

0
Community On

The problem should be coming from your models file. The NOT NULL constraint is set on a field which raises the error when you try to create it empty. So to overpass this, you need to set null=True. For example:

date_of_birth = models.DateField(blank=True, null=True)

Try looking at the models used in your create_chat_message function.

0
Bear Brown On

when you send the notification is read, you does not need all rest code, so you can simple add the return

if message_type == "notification_read":
    # Update the notification read status flag in Notification model.
    notification = Notification.object.get(id=notification_id)
    notification.notification_read = True
    notification.save()  #commit to DB
    print("notification read")
    return