【问题标题】:How to filter django model by its objects in many-to-many field (exact match)?如何通过多对多字段(精确匹配)中的对象过滤 django 模型?
【发布时间】:2012-11-15 04:59:54
【问题描述】:

我的代码中有这个模型:

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

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

我试过这样做:

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

其中 sender 是一个 User 对象,“recipients”是一个 User 对象的列表。 它不会引发错误,但它给了我错误的对话对象。 谢谢。

编辑: 最近的一次尝试让我明白了这一点:

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

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

编辑 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

【问题讨论】:

  • 这里也有question
  • 那里的答案不完整。我需要完全匹配用户。意思是检索一个包含我想要的用户的对话对象,它是多对多的,不是更少,不是更多。

标签: django django-models many-to-many django-orm


【解决方案1】:

好的,我找到了答案: 为了进行精确匹配,我必须对模型进行链式过滤,然后确保它具有所需的确切数量的参数,以便多对多字段将包含在其中 all 需要的对象,仅此而已。

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

得到了这段代码:

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】:

    为了使用数据库索引进行快速搜索,我使用以下代码:

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

    这会在保存时增加开销,但现在我可以使用这个 sn-p 执行快速过滤:

    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)
    

    【讨论】:

      猜你喜欢
      • 2020-09-25
      • 1970-01-01
      • 2016-10-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-07
      • 2015-07-18
      • 1970-01-01
      相关资源
      最近更新 更多