5

对于我正在处理的消息传递模块,我有这两个类。这个想法是对话由一组参与者(两个或更多)表示。我正在努力寻找一种通过逻辑查找对话的方法,即我试图找到的所需对话具有以下参与者。但是,我尝试Conversation.objects.filter(participants__in=[p1, p2])了 OR 样式查询,p1 是参与者或 p2 是参与者。我想要 p1 和 p2 并且... pN 是参与者。有什么帮助吗?

class Conversation(models.Model):
    date_started = models.DateTimeField(auto_now_add=True)
    participants = models.ManyToManyField(User)

    def get_messages(self):
        return Message.objects.filter(conversation=self)

    def new_message(self, sender, body):
        Message.objects.create(sender=sender, body=body, conversation=self)
        self.save()


class Message(models.Model):
    sender = models.ForeignKey(User)
    body = models.TextField()
    date = models.DateTimeField(auto_now_add=True)
    conversation = models.ForeignKey(Conversation)

    def __unicodde__(self):
        return body + "-" + sender 
4

4 回答 4

4

我认为你只需要迭代过滤。这可能完全是胡说八道,因为我有点睡眠不足,但也许是这样的经理方法:

class ConversationManager(models.Manager):
    def has_all(self, participants):
        # Start with all conversations
        reducedQs = self.get_query_set()
        for p in participants:
            # Reduce to conversations that have a participant "p" 
            reducedQs = reducedQs.filter(participants__id=p.id)
        return reducedQs

一般来说,您应该养成使用表级查询管理器方法的习惯,而不是类方法。通过这种方式,您会得到一个查询集,如果需要,您可以进一步过滤。

灵感来自对文档中具有成员名称 Paul 的所有组的查询和此答案

于 2012-12-02T07:33:58.440 回答
2

如果您在同一个相关模型上多次链接 filter(),则生成的查询将对同一个表有一个额外的 JOIN。

所以你有了 :Conversation.objects.filter(participants=p1).filter(participants=p2)

您可以通过查看生成的查询来确认此行为print Conversation.objects.filter(participants=p1).filter(participants=p2).query

请参阅:https ://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships

由于它仍然相当简单和高效,我会避免在查询后使用 python 逻辑,这需要从数据库中引入太多数据,然后通过迭代再次过滤。

于 2012-12-02T10:21:06.293 回答
0

One way of doing it could be using python sets:

#Get the set of conversation ids for each participant
    p1_conv_set = set(Converstation.objects.filter(participants = p1).values_list('id', flat=True))
    p2_conv_set = set(Converstation.objects.filter(participants = p2).values_list('id', flat=True))
    .
    .
    pn_conv_set = set(Converstation.objects.filter(participants = pN).values_list('id', flat=True))
    #Find the common ids for all participants
    all_participants_intersection = p1_conv_set & p2_conv_set & ....pN_conv_set
    #Get all the conversation for all the calculated ids
    Conversation.objects.filter(id__in = all_participants_intersection)
于 2012-12-01T23:32:30.247 回答
0

首先,我会在该字段中添加一个相关名称participants

participants = models.ManyToManyField(User, related_name='conversations')

这不是必需的,但更具可读性 IMO。

然后您可以执行以下操作:

p1.conversations.filter(participants__in=p2)

这将返回 p2 也参与的所有 p1 的对话。

我不确定这种过滤方法的数据库效率,也许使用其他类型的数据库(可能是 Neo4j 之类的图形数据库)更合适。

于 2012-12-01T21:30:16.477 回答