Django如何查询所有对象匹配的ManyToMany关系

时间:2018-11-20 15:18:31

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

我有以下型号:

## 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值。

如果有一种更好的方法来布置也可以工作的对象,那么我可以以此方式进行搜索。

4 个答案:

答案 0 :(得分:2)

我还没有测试过,但是我认为您可以执行以下操作:

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的命名模式。

答案 1 :(得分:0)

这不是最优雅的解决方案或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

答案 2 :(得分:0)

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

让您拥有以下型号:

class IssueTags(models.Model):
    name = models.CharField(max_length=400)

class Issues(models.Model):
    tags = models.ManyToManyField(IssueTags,blank = True)

您想获取包含所有个IssueTags = [“ 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

答案 3 :(得分:0)

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

对于以下型号:

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)