Django 模型上的独特风格过滤

发布于 2024-11-01 12:25:16 字数 1305 浏览 2 评论 0原文

对于我想要的东西,“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 并用它来注释一些东西。

我有点疯狂地试图让我的大脑了解细节。在我看来,这感觉像是你应该能够在几行内完成的事情,但我就是不知道如何做。

Distinct might be the wrong word for what I want but I have a Message class like the following for a simple flat messaging system between users:

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()

This allows two users to chat about a single Thread object. The system is designed to allow two users to have separate conversations on separate Threads.

So as it is, I can grab the messages a given user is involved in with the following query:

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

That outputs every message a user has sent or received. I'm building a page where users can see all their conversations with other users, grouped by thread. This is the ideal output that I can imagine getting to:

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

I have thought about iterating this from the top, but unless there's a way to filter through Thread into Message's to_user, from_user fields, there are just too many threads. The DB server would melt.

  • "Group" the messages by Thread
  • "Group" those by the other user so each group is between two distinct users, per Thread
  • Pluck the most recent to_user=u and annotate something with that.

I'm going a little crazy trying to warp my brain around the particulars. In my head it feels like something you should be able to do in a couple of lines but I just can't see how.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

万劫不复 2024-11-08 12:25:16
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 的每个线程。它们也都通过相同的查询(至少关于线程对象)进行过滤,以确保我们只返回与该查询相关的结果。

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

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

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

通过使用上述方法,无论如何,您每次都必须执行 2 次查询,首先是线程,然后是消息。但它永远不会超出这个范围(但要注意 select_lated 上的联接或相关对象上的递归查询)。

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

That might look a bit complex or scary but it should do what you are after. Essentially it queries all your threads first so we can find out what threads we've messaged about. Then it finds all the related messages to those threads.

Both of the queries are ordered by the same field so that in the lower part of the code we can iterate through the list only once, instead of needing a nested for loop to find each thread with the correct id. They are also both filtered by the same query (regarding thread objects at least) to ensure we are getting back only the relevant results to this query.

Lastly, the messages that we received back are grouped together and attached to each thread; they will show up in descending order for each thread.

Right at the end, you may also want to re-sort your threads to show the latest ones first, easy to do assuming a 'when' field on the Thread model:

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

By using the aforementioned method, you will have to do 2 queries every time, regardless, first the threads, then the messages. But it will never go above this (watch out for joins on a select_related though or recursive queries on related objects).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文