Как сделать много-много запросов Django, чтобы найти книгу с двумя авторами?
У меня есть запрос, который требует отфильтровать ровно 2 авторов с идентификатором
Теоретически
Book.objects.filter(author__id=1, author__id=2).
что невозможно.
Как я могу решить эту проблему?
Cheers,
Микки
Ответы
Ответ 1
Сначала не интуитивно, но ответ прямо перед нами.
Book.objects.filter(author__id=1).filter(author__id=2)
Если вы хотите получить точное соответствие, вы можете еще больше фильтровать этот результат теми элементами, которые имеют ровно 2 автора.
Book.objects.annotate(count=Count('author')).filter(author__id=1)\
.filter(author__id=13).filter(count=2)
Если вы хотите, чтобы точные соответствия были динамически, как насчет чего-то вроде этого?
def get_exact_match(model_class, m2m_field, ids):
query = model_class.objects.annotate(count=Count(m2m_field))\
.filter(count=len(ids))
for _id in ids:
query = query.filter(**{m2m_field: _id})
return query
matches = get_exact_match(MyModel, 'my_m2m_field', [1, 2, 3, 4])
# matches is still an unevaluated queryset, so you could run more filters
# without hitting the database.
Ответ 2
Новые вопросы указывают на этот вопрос как на дубликат, так что вот обновленный ответ (для одного конкретного бэкэнда).
Если бэкэнд является Postgres, то вам нужен SQL (при условии, что таблица M2M называется bookauthor
):
SELECT *
FROM book
WHERE
(SELECT ARRAY_AGG(bookauthor.author_id)
FROM bookauthor
WHERE bookauthor.book_id = book.id) = Array[1, 2];
Вы можете заставить Django генерировать почти этот SQL.
Во-первых, pip install django-sql-utils
. Затем создайте этот класс Array
:
from django.db.models import Func
class Array(Func):
function = 'ARRAY'
template = '%(function)s[%(expressions)s]'
И теперь вы можете написать свой набор запросов ORM:
from sql_util.utils import SubqueryAggregate
from django.contrib.postgres.aggregates import ArrayAgg
books = Book.objects.annotate(
author_ids=SubqueryAggregate('author__id', Aggregate=ArrayAgg)
).filter(author_ids=Array(1, 2))
Ответ 3
Объекты Q помогут вам. Docs
Book.objects.filter(Q(author__id=1) & Q(author__id=2))
Ответ 4
Вы можете использовать запрос "IN".
Django Docs
Book.objects.filter(author__id__in=[1,2])