【问题标题】:Distinct-style filtering on a Django modelDjango模型上的不同风格过滤
【发布时间】:2011-04-15 09:36:19
【问题描述】:

Distinct 可能是我想要的错误词,但我有一个类似以下的 Message 类,用于用户之间的简单平面消息传递系统:

class Message(models.Model):
    thread = models.ForeignKey(Thread)
    from_user = models.ForeignKey(User, related_name='messagefromuser')
    to_user = models.ForeignKey(User, related_name='messagetouser')
    when = models.DateTimeField(auto_now_add=True)
    message = models.TextField()

这允许两个用户就单个 Thread 对象聊天。该系统旨在允许两个用户在不同的线程上进行单独的对话。

因此,我可以通过以下查询获取给定用户所涉及的消息:

Message.objects.filter( Q(from_user=u) | Q(to_user=u) )

输出用户发送或接收的每条消息。我正在构建一个页面,用户可以在其中查看他们与其他用户的所有对话,按线程分组。这是我可以想象得到的理想输出:

[
    {
        'thread': thread_instance,
        'conversations': [
            {
                'other_user': user_instance
                'latest_reply': date_time_instance
            },
            ....
        ]
    },
    ...
]

我考虑过从顶部迭代这个,但除非有办法将Thread 过滤到Messageto_userfrom_user 字段中,否则线程太多了。数据库服务器会融化。

  • Thread“分组”消息
  • “分组”其他用户,因此每个组都在两个不同的用户之间,每个 Thread
  • 提取最新的to_user=u 并用它注释某些内容。

我有点疯狂地试图围绕细节扭曲我的大脑。在我的脑海里,这感觉就像你应该能够在几行代码中完成,但我就是不知道怎么做。

【问题讨论】:

  • 为什么不是线程的 to_user 和 from_user 属性?我可能会遗漏一些关于用例的内容,但似乎更容易获得您所描述的视图。然后,您仅在需要时才获得线程的消息。
  • @OmerGertel(令人困惑地)Thread 类实际上根本不是关于消息传递的。它是对话中的两个用户之一将在某个时候生成的对象。生成后,另一个用户可以与线程的所有者开始对话,然后所有者可以回复。如果系统是 StackOverflow,Thread 将是问题,Message 将是一种讨论问题的私人评论系统。
  • 但现在你已经说过了,我看到了一种可能性:我可以在 ThreadMessage 之间添加另一个层,称为 MessageThread 以在两者之间创建一个实用程序缓冲区(就像真正的消息传递线程会)。该系统还很年轻,所以如果它使事情变得更容易,我仍然可以负担得起冲洗数据。
  • MessageThread 可能是个好主意

标签: django django-models django-queryset


【解决方案1】:
threads = Thread.objects.filter(
  Q(message_set__from_user=u) | Q(message_set__to_user=u)
).order_by('id')

messages = Message.objects.filter(
  Q(thread__in=threads) & Q(Q(thread__from_user=u) | Q(thread_to_user=u))
).order_by('thread__id', '-when').select_related('from_user', 'to_user')

from itertools import groupby

t_index = 0
for thread_messages in groupby(messages, lambda x: x.thread_id):
  if threads[t_index].id is thread_messages[0].thread_id:
    threads[t_index].messages = thread_messages

  t_index += 1

这可能看起来有点复杂或可怕,但它应该做你所追求的。本质上,它首先查询您的所有线程,以便我们可以找出我们发送消息的线程。然后它会找到与这些线程相关的所有消息。

这两个查询都按相同的字段排序,因此在代码的下部,我们只能遍历列表一次,而不需要嵌套的 for 循环来查找具有正确 id 的每个线程。它们也都被同一个查询过滤(至少关于线程对象),以确保我们只返回与该查询相关的结果。

最后,我们收到的消息被组合在一起并附加到每个线程;它们将按每个线程的降序显示。

最后,您可能还想重新排序线程以首先显示最新的线程,假设线程模型上有一个“何时”字段,这很容易做到:

threads = sorted(threads, key=lambda x: x.when, reverse=True)

通过使用上述方法,您每次都必须进行 2 次查询,首先是线程,然后是消息。但它永远不会超过这个(注意 select_related 上的连接或相关对象上的递归查询)。

【讨论】:

    猜你喜欢
    • 2015-03-25
    • 1970-01-01
    • 2021-01-20
    • 2016-07-26
    • 2019-12-16
    • 2012-03-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多