0

我有一个包含三个字段的聊天消息模型:

发件人、收件人(用户模型的 ForeignKey)和作为 TextField 的消息。

我正在尝试选择与任一发件人或收件人字段(不包括 request.user)的所有唯一对话。我对如何实现它有点困惑。

我有2个问题:

Message.objects.filter(Q(sender = request.user)|Q(recipient = request.user)).values('sender').distinct()

不返回唯一记录列表(即使使用 order_by )。我有很多完全相同的发件人:({'sender': 4L}, {'sender': 4L}收件人也是如此)。

第二个问题是:

我是否需要连接两个 queysets(用于发件人和收件人)或者还有另一种方法来获取当前 request.user 的整个对话列表?

更新。好的,这是表格内容:

mysql> select id, sender_id, recipient_id, body from messages_message ;
+----+-----------+--------------+-----------+
| id | sender_id | recipient_id | body      |
+----+-----------+--------------+-----------+
|  1 |         4 |            1 | Message 1 |
|  2 |         4 |            1 | Message 2 |
+----+-----------+--------------+-----------+

这是结果

Message.objects.filter(Q(sender = request.user)|Q(recipient = request.user)).values('sender').distinct()

[{'sender': 4L}, {'sender': 4L}]

但我预计只会得到一个[{'sender': 4L}].

那么,怎么了?

更新2。我的模型:

class Message(models.Model):
    body = models.TextField(_("Body"))
    sender = models.ForeignKey(User, related_name='sent_messages', verbose_name=_("Sender"))
    recipient = models.ForeignKey(User, related_name='received_messages', null=True, blank=True, verbose_name=_("Recipient"))
    sent_at = models.DateTimeField(_("sent at"), null=True, blank=True)

我需要选择当前用户的所有对话伙伴(向 request.user 发送或接收消息的人)。

4

2 回答 2

1

正如 Rob 指出的那样,distinct()工作方式与您预期的不同。它查看所有字段以确定唯一性,而不仅仅是您在values().

如果您使用的是 PostgreSQL,那么您可以通过将参数传递给distinct(). 从文档中:

您可以传递位置参数 (*fields) 以指定应应用 DISTINCT 的字段的名称。这将转换为 SELECT DISTINCT ON SQL 查询。这就是区别。对于普通的 distinct() 调用,数据库在确定哪些行是不同的时比较每一行中的每个字段。对于具有指定字段名称的 distinct() 调用,数据库将仅比较指定的字段名称。

回到寻找所有对话伙伴的最终目标,我没有看到一个简单、优雅的解决方案。一种方法是使用聚合:

receivers = user.sent_messages.values('recipient')
                .aggregate(num_messages=Count('id'))
senders = user.received_messages.values('sender')
              .aggregate(num_messages=Count('id'))

如果您不关心发送者和接收者之间的区别,则需要手动组合它们。

于 2013-08-27T02:59:05.587 回答
1

只是我的$.02,我真的认为python比SQL更好地处理这种逻辑。如果您使用特定于一个数据库的查询参数,那么在我看来,它有点违背 ORM 的目的。

我会尝试这样的事情:

messages = Message.objects.filter(Q(sender = request.user)|Q(recipient = request.user))
## Does this need to be 'Q'? ##

然后:

partners = set()
for m in messages:
    partners.add(m.sender)
    partners.add(m.recipient)

如果你要经常看这个集合,你可以缓存它。

但最好是创建一个 的字段,在每次发送消息时添加到它。那么就不需要进行复杂的查询,只需一个简单的.partnersUserUser.partners

我假设您User无论如何都需要让对象发送消息,所以它不应该是额外的开销。

于 2013-08-27T13:29:52.563 回答