Допустим, у меня есть следующие модели
class Photo(models.Model):
tags = models.ManyToManyField(Tag)
class Tag(models.Model):
name = models.CharField(max_length=50)
В представлении у меня есть список с активными фильтрами, называемыми категориями . Я хочу отфильтровать фотообъекты, все теги которых присутствуют в категориях .
Я попытался:
Photo.objects.filter(tags__name__in=categories)
Но это соответствует любому элементу в категориях, а не всем элементам.
Итак, если категории будут ['праздник', 'лето'], я хочу, чтобы фотографии были отмечены как праздником, так и летом.
Можно ли этого добиться?
python
django
filter
django-queryset
Сандер ван Леувен
источник
источник
Photo.objects.filter(tags__name='holiday').filter(tags__name='summer')
это путь. (То же, что и в примере jpic). Каждыйfilter
должен добавить большеJOIN
s в запрос, чтобы вы могли использовать аннотационный подход, если их слишком много.Ответы:
Резюме:
Один из вариантов, предложенный jpic и sgallen в комментариях, можно добавить
.filter()
для каждой категории. Каждое дополнительноеfilter
добавляет больше объединений, что не должно быть проблемой для небольшого набора категорий.Есть агрегатный подход . Этот запрос будет короче и, возможно, быстрее для большого набора категорий.
У вас также есть возможность использовать собственные запросы .
Некоторые примеры
Испытательная установка:
Использование подхода с использованием цепных фильтров :
Результирующий запрос:
Обратите внимание, что каждый
filter
добавляет большеJOINS
к запросу.Использование аннотационного подхода :
Результирующий запрос:
AND
Q
объекты ed не будут работать:Результирующий запрос:
источник
t3
, а фотография будет иметь тегиt2
иt3
. Тогда эта фотография все равно будет соответствовать заданному запросу.Photo.objects.filter(tags__in=tags)
сопоставляет фотографии с любым из тегов, а не только с теми, на которых есть все. Некоторые из тех, у которых есть только один из желаемых тегов, могут иметь именно то количество тегов, которое вы ищете, а некоторые из тех, у которых есть все желаемые теги, могут также иметь дополнительные теги.Другой подход, который работает, хотя и только для PostgreSQL, заключается в использовании
django.contrib.postgres.fields.ArrayField
:Пример скопирован из документов :
ArrayField
имеет некоторые более мощные функции, такие как преобразование перекрытия и индекса .источник
Это также можно сделать с помощью динамической генерации запросов с использованием Django ORM и некоторой магии Python :)
Идея состоит в том, чтобы сгенерировать соответствующие объекты Q для каждой категории, а затем объединить их с помощью оператора AND в один QuerySet. Например, для вашего примера это будет равно
источник
filter
будет такой же, как использованиеand
для объектов Q в одном фильтре ... Моя ошибка.filter
кexclude
и использовать нивелирует оператор. Вот так:res = Photo.exclude(~reduce(and_, [Q(tags__name=c) for c in categories]))
Я использую небольшую функцию, которая выполняет итерацию фильтров по списку для заданного оператора и имени столбца:
и эту функцию можно вызвать так:
он также работает с любым классом и другими тегами в списке; операторы могут быть любыми, например 'iexact', 'in', 'contains', 'ne', ...
источник
источник
Если мы хотим делать это динамически, следуйте примеру:
источник