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

时间:2012-11-07 13:12:19

标签: django django-queryset manytomanyfield django-orm

我有这样的书模型:

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问题,但无法得到明确的答案。任何好的指针也会有所帮助。感谢。

4 个答案:

答案 0 :(得分:16)

与@ 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

答案 1 :(得分: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)

答案 2 :(得分:1)

Q()和Q()不等于.filter()。filter()。它们的原始SQL有所不同,通过将Q与&结合使用,其SQL仅添加了WHERE "book"."author" = "author_1" and "book"."author" = "author_2"之类的条件。它应该返回空结果。

唯一的解决方案是通过链接过滤器以在同一表... 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")上形成带有内部联接的SQL

答案 3 :(得分:0)

我遇到了同样的问题并得出与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。 但是,如果要正确应用优化,则必须使用准备好的QuerySet并正确应用索引。