Django 在 distinct() 后如何 order_by?还是使用 distinct?

0 投票
1 回答
639 浏览
提问于 2025-04-18 03:30

我需要一些像这样的东西:

user_messages = UserMessage.objects.filter(Q(from_user=user) | Q(to_user=user)).order_by('dialog_id').distinct('dialog_id').order_by('created')

当然,这个是不能用的。我发现我应该使用 annotate(),但是对我来说这似乎有点难,因为我刚开始学Django。你能帮我吗?

1 个回答

1

这是我用来实现类似功能的代码。也许对你有帮助。

在视图中:

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_loweruser_higher 之类的,以便更清楚。)

让我们来拆解一下视图代码:

获取所有对话对象,当前用户要么是 user_1,要么是 user_2。通过过滤,要求返回的对话对象中,至少有一些消息是当前用户可以看到的,并且这些消息没有被归档。对话没有时间戳,但消息有,所以根据最近的活动给对话列表添加时间戳注释。然后根据这个注释的时间戳进行排序。

这样做避免了任何 distinct() 的操作,因为你是通过对话列表进行连接查询,而不是通过消息列表进行连接查询。

撰写回答