如何将每个用户的最后一条消息显示给用户对话以保留聊天记录?

Posted

技术标签:

【中文标题】如何将每个用户的最后一条消息显示给用户对话以保留聊天记录?【英文标题】:How to show the last message of each user to user conversations to keep a chat history? 【发布时间】:2017-05-22 11:14:14 【问题描述】:

我正在创建一个私人用户到用户聊天,为了与某人聊天,连接的用户必须在他自己的 url 上输入他想与之交谈的用户的用户名。

现在这个系统已经建成,我想保留一个聊天记录,以便以后可以发送聊天通知。为此,我需要获取每个对话的最后一条消息,并希望将其显示在连接用户自己的聊天资料中。

如下图:

型号 userComment 字段为:recipientsendercommentsent_at

views.py

def inbox(request, username):
    username = User.objects.get(username=username)
    connected_user = request.user

    if username == connected_user:

        #I'm having the issue on this line
        users = userComment.objects.filter(Q(client=request.user) | Q(worker=request.user)).order_by(?) 

    else:
        users = userComment.objects.filter(Q(Q(client=request.user) & Q(worker=username)) | Q(Q(client=username) & Q(worker=request.user))).order_by('sent_at')

models.py

class userComment(models.Model):
    client = models.ForeignKey(User, related_name="client")
    worker = models.ForeignKey(User, blank=True, null=True, related_name="worker")
    sent_at = models.DateTimeField(auto_now_add=True)
    comment = models.TextField(max_length=255, null=True)

    def __str__(self):
        return str(self.client)

问题:如何过滤和排序我的视图?

【问题讨论】:

【参考方案1】:

首先在您的userComment 模型中为反向关系添加相关查询名称

class UserComment(models.Model):
    sender = models.ForeignKey(User, related_name='sender', related_query_name='s')
    recipient = models.ForeignKey(User, related_name='recipient', related_query_name='r')
    sent_at = models.DateTimeField(auto_now_add=True)
    comment = models.TextField()

现在在您的views.py 中使用此查询:

user = request.user

users = User.objects.filter(Q(r__sender=user) | Q(s__recipient=user)).distinct().extra(select='last_message_time': 'select MAX(sent_at) from appname_usercomment where (recipient_id=auth_user.id and sender_id=%s) or (recipient_id=%s and sender_id=auth_user.id)', select_params=(user.id, user.id,)).extra(order_by=['-last_message_time']).extra(select='message': 'select comment from appname_usercomment where (sent_at=(select MAX(sent_at) from appname_usercomment where (recipient_id=auth_user.id and sender_id=%s) or (recipient_id=%s and sender_id=auth_user.id)) and ((recipient_id=auth_user.id and sender_id=%s) or (recipient_id=%s and sender_id=auth_user.id)))',, select_params=(user.id, user.id,user.id, user.id,))

根据模型所在的app的名字额外设置appname。

现在您可以按如下方式访问它:

for user in users:
    print user.username
    print user.last_message_time
    print user.message

【讨论】:

【参考方案2】:
def inbox(request, username)    
    # first select all the comments related to user
    user = User.objects.get(username=username)
    related = userComment.objects.filter(q(client=user) | q(worker=user)).order_by('-sent_at')

    # This selects the latest comments.
    # Now loop over the related comments and group them.
    chats = 
    for comment in related:
        if comment.client == user:
            previous_chat_history = chats.setdefault(comment.worker.username, [])
            if not len(previous_chat_history) >= 3:
                previous_chat_history.append(comment)
        if comment.worker== user:
            previous_chat_history = chats.setdefault(comment.client.username, [])
            if not len(previous_chat_history) >= 3:
                previous_chat_history.append(comment)

    # Reverse each list to keep the latest message last
    for k, v in chats.items():
        chats[k] = v.reverse()

    return render(request, 'template.html', context=chats: chats) 

【讨论】:

不起作用,这只会获取所有查询的最后一项。我希望它能够获取每组不同消息的最后一项,而且如果它是按日期时间排序的,那么存在一个大问题,即您可能会在错误的收件箱中收到来自其他用户的消息。 @KasimovNuriddin 请发布用户评论模型 我已经发布了模型 我真的不明白发生了什么,请您发布完整视图应该如何?所以我可以理解它是如何放置的 聊天返回我:<User: user32>: None, <User: user1>: None,来自模板:private_comment.userComment.None【参考方案3】:

[更新]:我刚刚意识到此解决方案仅适用于 Postgresql,因为它使用 distinct 中的字段名称。


您可以混合使用order_bydistinct 以达到预期的效果:

    过滤用户为clientworker 的cmets:

    comments = userComment.objects.filter(Q(client=request.user) | Q(worker=request.user))
    

    使用clientworkersent_at 字段对用户cmets 进行排序。确保sent_at 字段的降序排列,以便每个客户端-工作人员对的最新 cmets 位于顶部:

    comments = comments.order_by('client', 'worker', '-sent_at')
    

    现在,获取不同的行:

    comments = comments.distinct('client', 'worker')
    

    这将只保留第一行,即每个客户端-工作者对的最新评论,并从查询集中删除其余行。


在一个声明中:

comments = userComment.objects \
              .filter(Q(client=request.user) | Q(worker=request.user)) \
              .order_by('client', 'worker', '-sent_at') \
              .distinct('client', 'worker')

这将为您提供用户为clientworker 的每个对话的最新评论。

【讨论】:

以上是关于如何将每个用户的最后一条消息显示给用户对话以保留聊天记录?的主要内容,如果未能解决你的问题,请参考以下文章

如何显示所有用户的最后一条消息?

Laravel 按最后一条消息对对话进行排序

如何获取每个用户的对话并包含消息?

如何使用 Telephony.Sms 仅检索每个对话的最后一条消息,包括锁定的消息?

如何使用 smack 和 openfire 获取 IOS 中每个聊天会话的最后一条消息?

当用户不在聊天中时如何显示发送的最后一条消息?