我有三个字段的聊天消息模型:
发件人,收件人(用户模型的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发送或接收邮件的人)。
答案 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
对象发送消息,所以它不应该是额外的开销。