Django Distinct 不只返回唯一字段



我有一个聊天消息模型,它有三个字段:

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

我正在尝试选择所有与发件人或收件人字段(exclude-request.user)的唯一对话。我在如何实现这一点上有点混乱。

我有两个问题:

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

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

第二个问题是:

我需要连接两个queyset(针对发件人和收件人)吗?或者有其他方法可以获得当前请求的整个对话列表。user?

upd。好的,这是表格内容:

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}]

那么,怎么了?

upd2.我的型号:

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)

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

就我的$.02而言,我真的认为python比SQL更好地处理这类逻辑。如果您使用特定于一个DB的查询参数,那么在我看来,这有点违背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)

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

但是,最好将partners设为User的字段,并在每次发送消息时将其添加到字段中。这样就不需要进行复杂的查询,只需要简单的User.partners

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

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

如果您使用的是PostgreSQL,那么您可以通过向distinct()传递参数来完成您想要的操作。来自文件:

您可以传递位置参数(*字段),以便指定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'))

如果你不在乎发送者和接收者之间的区别,那么你会想要手动组合它们。

最新更新