Как отфильтровать запрос так, чтобы вывелись посты по всем совпадениям тегов 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
Вернуться на верх