Как отфильтровать запрос так, чтобы вывелись посты по всем совпадениям тегов many to many, django
Как сделать запрос так, чтобы вывелись посты, которые совпадают по всем тегам, а не только по одному совпадению.
view.py
class CostTags:
def get_tags(self):
return TagsNotebook.objects.all()
def get_cost(self):
return CostNotebook.objects.all()
class Filter(CostTags, ListView):
template_name = 'poligloty_site/notebook.html'
context_object_name = 'notebook'
def get_queryset(self):
tags = self.request.GET.getlist("tag")
queryset = Notebook.objects.filter(tags__in=tags).distinct('id')
return queryset
urls.py
urlpatterns=[
path('', SborkiView.as_view(), name="notebook"),
path("filter/", Filter.as_view(), name='filter'),]
models.py
class TagsNotebook(models.Model):
tag = models.CharField('Тег', max_length=50)
url = models.SlugField(max_length=63, unique=True)
def __str__(self):
return self.tag
class Notebook(models.Model):
title = models.CharField('Название', max_length=50)
image_intro = models.ImageField('Image_intro', upload_to='intro/notebook')
anons = models.CharField('Кратко', max_length=250)
description = models.TextField('Описание')
date = models.DateTimeField('Публикация', auto_now_add=True)
tags = models.ManyToManyField("TagsNotebook", verbose_name='Теги')
cost = models.ForeignKey('CostNotebook', verbose_name='Категория стоимости', on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.title
class Meta:
verbose_name = 'Ноутбук'
verbose_name_plural = 'Ноутбуки'
sidebar.html
<form action="{% url 'filter' %}" method="get">
<div class="card mb-4">
<div class="card-header">Tags</div>
<div class="card-body">
<div class="row">
<div class="col-sm-6">
<ul class="list-unstyled mb-0">
{% for tags in view.get_tags %}
<li>
<input type="checkbox" class="checked" name="tag" value="{{ tags.id }}">
<a href="#">{{ tags.tag }}</a>
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
</div>
<button type="submit" >Search</button>
</form>
Получаются запросы:
INNER JOIN "mypc_site_notebook_tags"
ON ("mypc_site_notebook"."id" = "mypc_site_notebook_tags"."notebook_id")
WHERE "mypc_site_notebook_tags"."tagsnotebook_id" IN (2, 4)
Таким образом выводятся посты с тегами 2 или 4, а мне нужно, чтобы выводились с тегами 2 И 4.
Как это сделать?
Можно попробовать использовать reduce функцию для создания запроса. Например:
import operator
from functools import reduce
from django.db.models import Q
class Filter(CostTags, ListView):
template_name = 'poligloty_site/notebook.html'
context_object_name = 'notebook'
def get_queryset(self):
tags = self.request.GET.getlist("tag")
queryset = Notebook.objects.filter(
reduce(operator.and_, (Q(tag__pk=tag) for tag in tags)
)
return queryset