From 8a06133ae82df8c10eb4c94fa063499ad7a24098 Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sun, 3 May 2026 18:42:43 +0330 Subject: [PATCH] fix product filters --- backend/product/views.py | 63 ++++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/backend/product/views.py b/backend/product/views.py index 9f24514..5de5e55 100644 --- a/backend/product/views.py +++ b/backend/product/views.py @@ -20,6 +20,7 @@ from django.db.models import Min, Max, Count, Prefetch from home.models import ShowCaseSlider from home.serializers import ShowCaseSliderSerialzier from order.models import Cart, CartItem +from django.db.models import Min, Max, Value # class APIView(APIView): # def __init__(self, *args, **kwargs): # super().__init__(*args, **kwargs) @@ -272,61 +273,85 @@ class AllProductsView(APIView): if in_stock is not None: if in_stock.lower() == 'true': products = products.filter( - variants__in_stock__gt=0).distinct() + variants__in_stock__gt=0 + ).distinct() elif in_stock.lower() != 'false': - return Response({'detail': 'in_stock must be "true" or "false".'}, status=status.HTTP_400_BAD_REQUEST) + return Response( + {'detail': 'in_stock must be "true" or "false".'}, + status=status.HTTP_400_BAD_REQUEST + ) - # Filter by discount + # Discount filter has_discount = request.query_params.get('has_discount') if has_discount is not None: if has_discount.lower() == 'true': products = products.filter( - variants__discount__gt=0).distinct() + variants__discount__gt=0 + ).distinct() elif has_discount.lower() != 'false': - return Response({'detail': 'has_discount must be "true" or "false".'}, status=status.HTTP_400_BAD_REQUEST) + return Response( + {'detail': 'has_discount must be "true" or "false".'}, + status=status.HTTP_400_BAD_REQUEST + ) - # Search filter + # Search search_query = request.query_params.get('search') if search_query: products = products.annotate( similarity=( - TrigramSimilarity('name', search_query) - + TrigramSimilarity(Coalesce('description', Value('')), search_query) + TrigramSimilarity('name', search_query) + + TrigramSimilarity( + Coalesce('description', Value('')), + search_query + ) ) ).filter(similarity__gt=0.1) + # Price annotation (IMPORTANT for sorting) + products = products.annotate( + min_price=Min('variants__price'), + max_price=Max('variants__price') + ) + # Price filters price_gte = request.query_params.get('price_gte') - price_lte = request.query_params.get('price_lte') - - products = products.annotate(min_price=Min( - 'variants__price'), max_price=Max('variants__price')) - if price_gte: try: - price_gte = float(price_gte) - products = products.filter(max_price__gte=price_gte) + products = products.filter( + max_price__gte=float(price_gte) + ) except ValueError: - return Response({'detail': 'price_gte must be a number.'}, status=status.HTTP_400_BAD_REQUEST) + return Response( + {'detail': 'price_gte must be a number.'}, + status=status.HTTP_400_BAD_REQUEST + ) + price_lte = request.query_params.get('price_lte') if price_lte: try: - price_lte = float(price_lte) - products = products.filter(min_price__lte=price_lte) + products = products.filter( + min_price__lte=float(price_lte) + ) except ValueError: return Response({'detail': 'price_lte must be a number.'}, status=status.HTTP_400_BAD_REQUEST) # Sorting sort_by = request.query_params.get('sort') + if sort_by == 'newest': + sort_by = '-created_at' + elif sort_by == 'oldest': + sort_by = 'created_at' if sort_by in ['name', '-name', 'created_at', '-created_at']: products = products.order_by(sort_by) + + elif sort_by in ['price', '-price']: + products = products.order_by('min_price' if sort_by == 'price' else '-min_price') elif search_query: products = products.order_by('-similarity', 'name') else: products = products.order_by('name') # Pagination - products.order_by('category') paginator = self.pagination_class() paginated_products = paginator.paginate_queryset(products, request) serializer = self.serializer_class(