如何根据多对多字段(精确匹配)中的对象过滤django模型?

时间:2022-10-04 13:56:28

I have this model in my code:

我的代码中有这个模型:

class Conversation(models.Model):
    participants = models.ManyToManyField(User, related_name="message_participants")

and I need to filter this "Conversation" model objects by the "participants" many-to-many field. meaning: I have for example 3 User objects, so I want to retrieve the only "Conversation" objects that has this 3 Users in it's "participants" field.

我需要通过“参与者”多对多字段过滤这个“对话”模型对象。含义:例如,我有3个用户对象,所以我想检索唯一的“对话”对象,它的“参与者”字段中有3个用户。

I tried doing this:

我试着这样做:

def get_exist_conv_or_none(sender,recipients):
    conv = Conversation.objects.filter(participants=sender)
    for rec in recipients:
        conv = conv.filter(participants=rec)

where sender is a User object and "recipients" is a list of User objects. it won't raise error but it gives me the wrong Object of Conversation. Thanks.

发送方是用户对象,“收件人”是用户对象的列表。它不会引起错误,但它给了我错误的谈话对象。谢谢。

edit: A more recent try lead me to this:

编辑:最近的一次尝试让我得出以下结论:

def get_exist_conv_or_none(sender,recipients):
    participants=recipients
    participants.append(sender)
    conv = Conversation.objects.filter(participants__in=participants)
    return conv

which basically have the same problem. It yields Objects which has one or more of the "participants" on the list. but what Im looking for is exact match of the many-to-many object. Meaning, an Object with the exact "Users" on it's many-to-many relation.

基本上都有相同的问题。它生成具有列表上的一个或多个“参与者”的对象。但是我要找的是多对多对象的精确匹配。意思是,一个对象在它的多对多关系上有确切的“用户”。

edit 2: My last attempt. still, won't work.

编辑2:我的最后一次尝试。不过,不能工作。

def get_exist_conv_or_none(sender,recipients):
    recipients.append(sender)
    recipients = list(set(recipients))
    conv = Conversation.objects.annotate(count=Count('participants')).filter(participants=recipients[0])
    for participant in recipients[1:]:
        conv.filter(participants=participant)
    conv.filter(count=len(recipients))
    return conv

2 个解决方案

#1


6  

Ok so I found the answer: In order to make an exact match I have to chain-filter the model and then make sure it has the exact number of arguments it needs to have, so that the many-to-many field will have in it all the objects needed and no more.

我找到了答案:为了进行精确的匹配,我必须对模型进行链式筛选,然后确保它有确切的参数数目,这样多对多字段就会包含所有需要的对象,而不是更多。

I will check for the objects number using annotation: ( https://docs.djangoproject.com/en/dev/topics/db/aggregation/ )

我将使用annotation检查对象编号:(https://docs.djangoproject.com/en/dev/topics/db/aggregation/)

ended up with this code:

以以下代码结束:

def get_exist_conv_or_none(recipients):
    conv = Conversation.objects.annotate(count=Count('participants')).filter(participants=recipients[0])
    for participant in recipients[1:]:
        conv = conv.filter(participants=participant)
    conv = conv.filter(count=len(recipients))
    return conv

#2


2  

For fast search using database index, I use this code:

对于使用数据库索引的快速搜索,我使用以下代码:

class YandexWordstatQueue(models.Model):
    regions = models.ManyToManyField(YandexRegion)
    regions_cached = models.CharField(max_length=10000, editable=False, db_index=True)
    phrase = models.ForeignKey(SearchPhrase, db_index=True)
    tstamp = models.DateTimeField(auto_now_add=True)

class YandexWordstatRecord(models.Model):
    regions = models.ManyToManyField(YandexRegion)
    regions_cached = models.CharField(max_length=10000, editable=False, db_index=True)
    phrase = models.ForeignKey(SearchPhrase, db_index=True)
    Shows = models.IntegerField()
    date = models.DateField(auto_now_add=True)

@receiver(m2m_changed, sender=YandexWordstatRecord.regions.through)
@receiver(m2m_changed, sender=YandexWordstatQueue.regions.through)
def yandexwordstat_regions_changed(sender, **kwargs):
    if kwargs.get('action') in ['post_add', 'post_remove']:
        instance = kwargs.get('instance')
        l = list(instance.regions.values_list('RegionID', flat=True))
        l.sort()
        instance.regions_cached = json.dumps(l)
        instance.save()

This adds overhead when saving, but now I can perform fast filter with this snippet:

这增加了保存时的开销,但是现在我可以使用这个片段执行快速筛选:

region_ids = [1, 2, 3] # or list(some_queryset.values_list(...))
region_ids.sort()
regions_cahed = json.dumps(region_ids)
YandexWordstatQueue.objects.filter(regions_cached=regions_cached)

#1


6  

Ok so I found the answer: In order to make an exact match I have to chain-filter the model and then make sure it has the exact number of arguments it needs to have, so that the many-to-many field will have in it all the objects needed and no more.

我找到了答案:为了进行精确的匹配,我必须对模型进行链式筛选,然后确保它有确切的参数数目,这样多对多字段就会包含所有需要的对象,而不是更多。

I will check for the objects number using annotation: ( https://docs.djangoproject.com/en/dev/topics/db/aggregation/ )

我将使用annotation检查对象编号:(https://docs.djangoproject.com/en/dev/topics/db/aggregation/)

ended up with this code:

以以下代码结束:

def get_exist_conv_or_none(recipients):
    conv = Conversation.objects.annotate(count=Count('participants')).filter(participants=recipients[0])
    for participant in recipients[1:]:
        conv = conv.filter(participants=participant)
    conv = conv.filter(count=len(recipients))
    return conv

#2


2  

For fast search using database index, I use this code:

对于使用数据库索引的快速搜索,我使用以下代码:

class YandexWordstatQueue(models.Model):
    regions = models.ManyToManyField(YandexRegion)
    regions_cached = models.CharField(max_length=10000, editable=False, db_index=True)
    phrase = models.ForeignKey(SearchPhrase, db_index=True)
    tstamp = models.DateTimeField(auto_now_add=True)

class YandexWordstatRecord(models.Model):
    regions = models.ManyToManyField(YandexRegion)
    regions_cached = models.CharField(max_length=10000, editable=False, db_index=True)
    phrase = models.ForeignKey(SearchPhrase, db_index=True)
    Shows = models.IntegerField()
    date = models.DateField(auto_now_add=True)

@receiver(m2m_changed, sender=YandexWordstatRecord.regions.through)
@receiver(m2m_changed, sender=YandexWordstatQueue.regions.through)
def yandexwordstat_regions_changed(sender, **kwargs):
    if kwargs.get('action') in ['post_add', 'post_remove']:
        instance = kwargs.get('instance')
        l = list(instance.regions.values_list('RegionID', flat=True))
        l.sort()
        instance.regions_cached = json.dumps(l)
        instance.save()

This adds overhead when saving, but now I can perform fast filter with this snippet:

这增加了保存时的开销,但是现在我可以使用这个片段执行快速筛选:

region_ids = [1, 2, 3] # or list(some_queryset.values_list(...))
region_ids.sort()
regions_cahed = json.dumps(region_ids)
YandexWordstatQueue.objects.filter(regions_cached=regions_cached)