Django:ManyToMany过滤器匹配列表中的所有项目

Posted

技术标签:

【中文标题】Django:ManyToMany过滤器匹配列表中的所有项目【英文标题】:Django: ManyToMany filter matching on ALL items in a list 【发布时间】:2012-10-27 13:20:24 【问题描述】:

我有这样一个 Book 模型:

class Book(models.Model):
    authors = models.ManyToManyField(Author, ...)
    ...

简而言之:

我想检索其作者严格等于一组给定作者的书籍。我不确定是否有一个查询可以做到这一点,但任何建议都会有所帮助。

长篇大论:

这是我尝试过的,(运行失败,出现 AttributeError)

# A sample set of authors
target_authors = set((author_1, author_2))

# To reduce the search space, 
# first retrieve those books with just 2 authors.
candidate_books = Book.objects.annotate(c=Count('authors')).filter(c=len(target_authors))

final_books = QuerySet()
for author in target_authors:
    temp_books = candidate_books.filter(authors__in=[author])
    final_books = final_books and temp_books

...这是我得到的:

AttributeError: 'NoneType' object has no attribute '_meta'

一般情况下,我应该如何查询具有ManyToMany字段包含一组给定对象的约束的模型?

ps:我发现了一些相关的 SO 问题,但无法得到明确的答案。任何好的指针也会有所帮助。谢谢。

【问题讨论】:

快到了。查看这个问题的答案:***.com/questions/8618068/… 【参考方案1】:

Q() & Q() 不等于 .filter().filter()。他们的原始 SQL 是不同的,其中使用 Q 和 &,它的 SQL 只是添加一个条件,如WHERE "book"."author" = "author_1" and "book"."author" = "author_2"。它应该返回空结果。

唯一的解决方案是通过链接过滤器在同一张表上形成一个带有内连接的 SQL:... ON ("author"."id" = "author_book"."author_id") INNER JOIN "author_book" T4 ON ("author"."id" = T4."author_id") WHERE ("author_book"."author_id" = "author_1" AND T4."author_id" = "author_1")

【讨论】:

【参考方案2】:

我遇到了同样的问题,得出了和iuysal一样的结论, 直到我不得不进行中型搜索(有 1000 条记录和 150 个过滤器,我的请求会超时)。

在我的特定情况下,搜索将导致没有记录,因为单个记录与所有 150 个过滤器对齐的机会非常少,您可以通过在应用更多之前验证 QuerySet 中有记录来解决性能问题过滤器以节省时间。

# In each iteration, we filter out those books which don't contain one of the 
# required authors - the instance on the iteration.
for author in target_authors:
   if candidate_books.count() > 0:
      candidate_books = candidate_books.filter(authors=author)

出于某种原因,Django 将过滤器应用于空查询集。 但是,如果要正确应用优化,则需要使用准备好的 QuerySet 和正确应用的索引。

【讨论】:

【参考方案3】:

类似于@goliney 的方法,我找到了解决方案。但是,我认为效率可以提高。

# A sample set of authors
target_authors = set((author_1, author_2))

# To reduce the search space, first retrieve those books with just 2 authors.
candidate_books = Book.objects.annotate(c=Count('authors')).filter(c=len(target_authors))

# In each iteration, we filter out those books which don't contain one of the 
# required authors - the instance on the iteration.
for author in target_authors:
    candidate_books = candidate_books.filter(authors=author)

final_books = candidate_books

【讨论】:

您的解决方案也是如此。过滤器 kwargs 是“AND”ed 你说得对,它们看起来一样。但是,我认为存在执行差异。据我了解,在您的方法中,与author_1 匹配的authors 字段中的author 也有望与author_2 匹配。另一方面,迭代过滤不强制执行这样的约束。如果我错了,请纠正我。我是来学习的。再次感谢!【参考方案4】:

您可以使用complex lookups with Q objects

from django.db.models import Q
...
target_authors = set((author_1, author_2))
q = Q()
for author in target_authors:
    q &= Q(authors=author)
Books.objects.annotate(c=Count('authors')).filter(c=len(target_authors)).filter(q)

【讨论】:

感谢@goliney,虽然方法简洁且鼓舞人心,但我想它并没有达到我想要的效果。如果只有一个作者,它可以正常工作,但是当有多个作者时,AND 过程可能会导致不可能的约束,例如 (其中 a=x AND a=y)。 事实证明,Q() 行为对我来说有些不清楚。根据this relative question我在SO上找到,Q() & Q()不等于.filter().filter()。谢谢你的提问 感谢您的链接和分享您的时间。

以上是关于Django:ManyToMany过滤器匹配列表中的所有项目的主要内容,如果未能解决你的问题,请参考以下文章

django - manytomany 上的查询过滤器为空

在 Django Admin 中过滤多对多框

Django在ManyToMany计数上过滤模型?

使用related_name将Django管理员中的ManyToMany呈现为水平过滤器

Django DRF视图过滤ManyToMany查询集

Django:模型设计,ManyToMany 属性字段?