Dave
Dave

Reputation: 23

Django - Private messaging conversation view

I have built a very basic private messaging module for my Django project.

I have a Message model which consists of:

sender (Foreign key to the member model)
recipient (Foreign key to the member model)
message
date (Datetime of which the message was created)

Now my issue is i would like to create a new view which returns a list of conversations based on these messages.

I am trying to write a query which returns the latest messages but unique where sender = current user OR recipient = current user. This is so that i have a list of latest messages, which should be the equivalent of a conversation list. Have i got that bit right or am i completely over thinking things?

conversations = Message.objects.filter(Q(recipient=request.user) | 
Q(sender=request.user)).annotate(max=Max('date'))

but this is returning duplicate conversations I tried this from another stack overflow post:

conversations = Message.objects.order_by('recipient', 'sender', 
'date').distinct('recipient', 'sender') 

but I'm receiving this error "DISTINCT ON fields is not supported by this database backend"

Any help would be greatly appreciated.

Upvotes: 2

Views: 2053

Answers (1)

Brendan Goggin
Brendan Goggin

Reputation: 2361

As @Grimmy stated in the comments, please post more information. Specifically, please add to your answer the query you tried (which you already have in the comments) and the result, and what is wrong with the result (you say "duplicate converstations; do you mean each conversation shows up just twice, or more than twice?)

Two options come to mind for this situation:

  1. You could run a query on your existing model that removes/excludes duplicates.
  2. You could create a new model called Conversation which holds information about the conversation, including members, date started, etc., and add a foreign key to each message assigning it one conversation (many-to-one from message to conversation). This would simplify your queries and would be easily extendable to accommodate "group chats" if need be (just add more members to the conversation), or things like a picture or title for the conversation itself.

When you post more information, I'll address option one in more detail if you would like. For now, I'll just say that if you are getting duplicate conversations where each conversation only shows up twice (once where the user is the sender and once where the user is the recipient), then it sounds like your query is good enough and you can just write a for loop in python that sorts through the resulting conversations and removes the older duplicates. As long as each user is in no more than over one or two hundred conversations, which sounds like the case, that shouldn't slow down performance too much.

But in any event, I recommend option two. I had to implement a similar feature in a Django project, and I went with option 2. It greatly simplifies your query to look like this:

# get the conversations the user is in
conversation_list = Message.objects.filter(conversation__participants=user).order_by('-date')

# get a list of the most recent message of each conversation
message_list = conversation_list.values('conversation').annotate(
    first_msg=Max('conversation__message')
)

In order for that second line to sort messages properly, add the following to your Message model:

class Message(models.Model):
    # sender field is unchanged
    # date field is unchanged (maybe rename to avoid name collisions with date module)
    # message field is unchanged (maybe rename to 'content' to avoid confusion)

    # make recipient many-to-many because I'd recommend having both the sender and the recipient listed as recipients,
    # but you don't have to do that
    recipient = models.ManyToManyField(User)

    # conversation foreign key
    conversation = models.ForeignKey(Conversation, blank=False, null=False)

    # this simplifies sorting so the "Max('conversation__message')" line 
    # sorts on date rather than primary key
    ordering = ["-date"]

Here is the Conversation model:

class Conversation(models.Model):
    participants = models.ManyToManyField(User)

    # example functionality you may wish to add later
    group_name = models.CharField(max_length=512, default="Group", blank=False, null=False)
    profile_picture = models.FileField(upload_to='uploads/', default='uploads/GroupChatIcon.jpg')

Upvotes: 6

Related Questions