Подтвердить что ты не робот

Django: сопоставление фильтров ManyToMany по всем элементам в списке

У меня есть такая модель книги:

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

Короче:

Я хотел бы получить книги, авторы которых строго равны заданному набору авторов. Я не уверен, что есть один запрос, который делает это, но любые предложения будут полезны.

В длинном:

Вот что я пробовал (не удалось запустить Get 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, но не смог получить четкого ответа. Любой хороший указатель будет полезен. Спасибо.

4b9b3361

Ответ 1

Подобно подходу @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

Ответ 3

Я столкнулся с одной и той же проблемой и пришел к тому же выводу, что и iyysal, пока мне не пришлось делать поиск среднего размера (с 1000 записями с 150 фильтрами мой запрос будет тайм-аут).

В моем конкретном случае поиск приведет к отсутствию записей, поскольку вероятность того, что одна запись будет выровнена со всеми фильтрами 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 применяет фильтры для удаления QuerySets. Но если оптимизация должна применяться правильно, однако, необходимо использовать подготовленный QuerySet и правильно применяемые индексы.

Ответ 4

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