【问题标题】:Django How To Query ManyToMany Relationship Where All Objects MatchDjango如何查询所有对象匹配的多对多关系
【发布时间】:2019-04-23 01:57:46
【问题描述】:

我有以下型号:

## Tags for issues
class issueTags(models.Model):
    name = models.CharField(max_length=400)
class issues(models.Model):
    tags = models.ManyToManyField(issueTags,blank = True)

在我看来,我从一些客户端 JavaScript 得到一个数组,即

(Pdb) array_data = request.POST['arr']
(Pdb) array_data
'["2","3"]'

我应该如何过滤问题对象以查找与数组中所有标签匹配的所有问题? (2,3 是 tag__id 的 ID 值。

如果有更好的方法来安排也可以工作的对象,那么我可以以这种方式进行搜索。

【问题讨论】:

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


    【解决方案1】:

    我没有对此进行测试,但我认为您可以执行以下操作:

    from django.db.models import Q
    
    array_data = array_data.split(',')
    issues.objects.filter(
        tags__in=array_data,
    ).exclude(
        # Exclude any that aren't in array_data
        ~Q(tags__in=array_data)
    ).annotate(
        matches=Count(tags, distinct=True)
    ).filter(
        # Make sure the number found is right.
        matches=len(array_data)
    )
    

    仅供参考,您应该使用 IssueIssueTag 作为模型名称,以遵循 Django 的命名模式。

    【讨论】:

    • 感谢您的回答,我遇到的问题是它仍然是一个或样式声明,其中 tags__in 引入了任何只有一个标签并且排除似乎没有抛出的问题那些没有所有标签的。可能只是我的测试场景有缺陷。也感谢您对案例的评论:)
    【解决方案2】:

    这不是最优雅的解决方案或 Pythonic,但我最终只是在生成的过滤器周围循环。

    def filter_on_category(issue_object,array_of_tags):
        #keep filtering to make an and
        i = 0
        current_filter = issue_object
        while (i < (len(array_of_tags))):
            #lets filter again
            current_filter=current_filter.filter(tags__id__in=array_of_tags[i])
            i=i+1
    
        return current_filter
    

    【讨论】:

      【解决方案3】:

      多对多字段的 Django 字段查找参数 (__) 需要列表参数。我为 IssueTags 的每个数组元素创建了一个虚拟列表,并将其传递给 lookups 参数,它按预期工作。

      让你拥有这个模型:

      class IssueTags(models.Model):
          name = models.CharField(max_length=400)
      
      class Issues(models.Model):
          tags = models.ManyToManyField(IssueTags,blank = True)
      

      您想要获取包含所有这些问题标签的问题 = ["1","2","3"]

      issue_tags_array = ["1","2","3"]
      
      #First initialize queryset 
      queryset = Issues.objects.all() 
      i = 0 
      while i < len(issue_tags_array): 
        #dummy issue_tag list
        issue_tag = [issue_tags_array[i]]
        #lets filter again 
        queryset = queryset.filter(tags__id__in=issue_tag) 
        i=i+1
      return queryset
      
      

      【讨论】:

        【解决方案4】:

        在撰写本文时,现有答案要么不正确(例如过滤匹配具有任何指定标签和正确标签计数的所有问题)或效率低下(例如在循环中附加过滤器)。

        适用于以下型号:

        class IssueTag(models.Model):
            name = models.CharField(max_length=400, blank=True)
        
        class Issue(models.Model):
            label = models.CharField(max_length=50, blank=True)
            tags = models.ManyToManyField(IssueTag, related_name='issues')
        

        我建议将 Django Annotation 与这样的过滤器结合使用:

        from django.db.models import Count, Q
        
        tags_to_match = ['tag1', 'tag2']
        
        issues_containing_all_tags = Issue.objects \
            .annotate(num_correct_tags=Count('tags',
                                             filter=Q(tags__name__in=tags_to_match))) \
            .filter(num_correct_tags=2)
        

        获取具有所有必需标签的所有问题(但可能有其他标签,如问题中所要求的那样)。

        这将产生以下 SQL 查询,解析单个 IN 子句中的所有标记匹配:

        SELECT "my_app_issue"."id", "my_app_issue"."label", 
            COUNT("my_app_issue_tags"."issuetag_id") 
                FILTER (WHERE "my_app_issuetag"."name" IN ('tag1', 'tag2'))
                AS "num_correct_tags"
        FROM "my_app_issue"
        LEFT OUTER JOIN "my_app_issue_tags" ON ("my_app_issue"."id" = "my_app_issue_tags"."issue_id")
        LEFT OUTER JOIN "my_app_issuetag" ON ("my_app_issue_tags"."issuetag_id" = "my_app_issuetag"."id")
        GROUP BY "my_app_issue"."id", "my_app_issue"."label"
        HAVING COUNT("my_app_issue_tags"."issuetag_id")
        FILTER (WHERE ("my_app_issuetag"."name" IN ('tag1', 'tag2'))) = 2; 
        args=('tag1', 'tag2', 'tag1', 'tag2', 2)
        

        【讨论】:

          猜你喜欢
          • 2020-06-08
          • 2022-10-07
          • 2011-10-09
          • 1970-01-01
          • 2017-02-02
          • 1970-01-01
          • 2015-10-26
          • 1970-01-01
          • 2011-03-17
          相关资源
          最近更新 更多