[英]Django M2M QuerySet filtering on multiple foreign keys
对于正在使用的消息传递模块,我有这两个类。 想法是,对话由一组参与者(两个或更多)代表。 我正在努力寻找一种查找对话的方法,其逻辑是说我要查找的期望对话包含以下参与者。 我尝试了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
我认为您只需要迭代过滤。 这可能完全是胡说八道,因为我有点睡眠不足,但也许是这样的管理器方法:
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
一般来说,您应该养成使用表级查询管理器方法的习惯,而不是使用类方法。 通过这种方式,您将获得一个查询集,可以根据需要进一步过滤。
如果您在相同的相关模型上多次链接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逻辑,这将需要从数据库中引入过多数据,然后通过迭代再次进行过滤。
首先,我要在participants
字段中添加一个相关名称:
participants = models.ManyToManyField(User, related_name='conversations')
这不是必需的,但更具可读性的IMO。
然后,您可以执行以下操作:
p1.conversations.filter(participants__in=p2)
这将返回p2也参与的所有p1对话。
我不确定这种过滤方法的数据库效率,也许使用其他类型的数据库(例如图数据库,例如Neo4j)更合适。
一种方法是使用python集:
#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)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.