How to change Q from AND to OR - Python with Django

I have a simple E-commerce where I want a product filter for users. In this case I want to filter the cars by color through a checkbox form. But when I do the for on the selected colors, it creates a Q(AND: ) filter, and I wanted it to be a Q(OR: ) filter. How do I change it ???

Index.html:

<div class="widgets-area mb-9">
  <h2 class="widgets-title mb-5">Cores</h2>
  <div class="widgets-item">
      <form id="widgets-checkbox-form" action="{% url 'carro_filtro' %}" method="GET">
        <ul class="widgets-checkbox">
          {% for cor in cores %}
          <li>
              <input class="input-checkbox" type="checkbox" id="color-selection-{{ cor.id }}" name="termo" value="{{ cor.nome_cor }}">
              <label class="label-checkbox mb-0" for="color-selection-{{ cor.id }}">
                {{ cor.nome_cor }}
              </label>
          </li>
          {% endfor %}
        </ul>
        <input type="submit" class="btn btn-custom-size lg-size btn-primary w-100 mb-5 mt-5" value="Filtrar">
      </form>
  </div>
</div>

Urls.py:

from django.urls import path
from . import views

urlpatterns = [
    path('', views.CarView.as_view(), name='shop'),
    path('filtro/', views.CarroFiltro.as_view(), name='carro_filtro'),
]

Views.py:

class CarView(ListView):
    model = Car
    template_name = 'shop/index.html'
    paginate_by = 12
    context_object_name = 'cars'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['personalizacoes'] = Shop.objects.filter(
            publicado_shop=True).order_by('-id').first()
        context['categorias'] = Categoria.objects.all()
        context['cores'] = Cor.objects.all()

        return context

    def get_queryset(self):
        qs = super().get_queryset()
        categoria = self.kwargs.get('nome_categoria', None)

        if not categoria:
            qs = qs.filter(publicado=True).order_by('-id')
            return qs

        qs = qs.filter(
            categoria_carro__nome_categoria__iexact=categoria, publicado=True).order_by('-id')
        return qs


class CarroFiltro(CarView):

    def get_queryset(self):
        qs = super().get_queryset()
        queries = []
        selected_colors = self.request.GET.getlist('termo')

        for selected_color in selected_colors:
            if selected_color is not None:
                queries.append(Q(cor__nome_cor__icontains=selected_color))

        qs = qs.filter(*queries)

        print('X'*100)

        print('1)')
        print(queries)

        print('X'*100)

        print('2)')
        print(*queries)

        print('X'*100)

        return qs

Views.py print results: enter image description here

In short

I'm getting:

(AND: ('cor__nome_cor__icontains', 'Branco')) (AND: ('cor__nome_cor__icontains', 'Preto'))

I want to get:

(OR: ('cor__nome_cor__icontains', 'Branco'), ('cor__nome_cor__icontains', 'Preto'))

Rather than

qs = qs.filter(*queries)

I would suggest something like

from functools import reduce
import operator

combined_q_expr = reduce(operator.or_, queries)
qs = qs.filter(combined_q_expr)

(Put the imports at the top of the file.)

This is the same thing as doing

Q(value=foo) | Q(value=bar | Q(price=10)

for all of your Q objects.

Back to Top