这是我必须实现类似功能的代码。也许对你有用。
在视图中:
queryset = Conversation.objects.filter(Q(user_1=user) | Q(user_2=user)).filter(
Q(messages__sender=user, messages__archived_by_sender=False) |
Q(messages__recipient=user, messages__archived_by_recipient=False)).annotate(
last_activity=Max("messages__created")).order_by("-last_activity")
模型看起来像:(省略方法等)
class Conversation(models.Model): # there is only one of these for each pair of users who message one another
user_1 = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="conversations_as_user_1")
user_2 = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="conversations_as_user_2")
read_by_user_1 = models.BooleanField(default=False)
read_by_user_2 = models.BooleanField(default=False)
class Meta:
unique_together = (("user_1", "user_2"),)
class Message(TimeTrackable):
conversation = models.ForeignKey(Conversation, related_name="messages", blank=True, help_text="the conversation between these two users (ALWAYS selected automatically -- leave this blank!)")
sender = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="messages_sent")
recipient = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="messages_received")
text = models.TextField() # flat text, NOT bleached or marksafe
archived_by_sender = models.BooleanField(default=False)
archived_by_recipient = models.BooleanField(default=False)
这适用于一个应用程序,您永远不会在同一用户之间拥有多个单独的对话对象,但您可以使用存档功能来存档所有消息(从您的角度来看),这与从用户的视角。此外,“已读”状态存储在对话中,而不是消息中。
为了保证两个用户之间不会有多个“对话”对象,对话中的 user_1 始终是较低的用户 ID,而 user_2 始终是较高的用户 ID。 (事实上,我并没有及时想到这个聪明的想法来实际实现它,所以我有复杂且不必要的覆盖 save() 逻辑。但如果我再做一次,我也会做这部分,也许甚至调用字段user_lower 和user_higher 或类似的名称以使其清楚。)
让我们分解视图代码:
获取当前用户为 user_1 或 user_2 的所有对话对象。通过过滤器,要求返回的任何对话对象至少有一些对当前用户可见的消息尚未归档。对话没有时间戳,但消息有,因此请按最近的活动对对话列表进行注释。然后按带注释的时间戳排序。
这避免了任何 distinct() 工作,因为您是在带有连接的对话列表上而不是在带有连接的消息列表上提取。