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.