django distinct不会返回唯一的字段

时间:2013-08-26 15:35:34

标签: django django-views distinct

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

发件人,收件人(用户模型的ForeignKey)和作为TextField的邮件。

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

我有两个问题:

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

不返回唯一记录列表(即使使用order_by)。我有很多绝对相同的发件人:{'sender': 4L}, {'sender': 4L}(与收件人一样)。

第二个问题是:

我是否需要连接两个queysets(对于发件人和收件人),或者还有另一种方法可以获取当前request.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)

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

2 个答案:

答案 0 :(得分:1)

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

如果您正在使用PostgreSQL,那么您可以通过将参数传递给distinct()来执行您想要的操作。来自documentation

  

您可以传递位置参数(*字段)以指定   DISTINCT应该应用的字段的名称。这转化为   SQL查询的SELECT DISTINCT。这是区别。对于正常   distinct()调用,数据库比较每行中的每个字段   确定哪些行是不同的。对于distinct()调用   指定的字段名称,数据库只会比较指定的   字段名称。

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

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

如果您不关心发件人和收件人之间的区别,那么您希望手动合并它们。

答案 1 :(得分: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)

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

但是可能更好地使partners成为User的字段,并在每次发送消息时添加它。然后,不需要进行复杂的查询,只需要一个简单的User.partners

我认为你需要让User对象发送消息,所以它不应该是额外的开销。