From 3391664923ff02ecd9e0a07dc9e970fbf7073f1a Mon Sep 17 00:00:00 2001 From: mohsentaba Date: Wed, 7 Oct 2026 11:02:04 +0330 Subject: [PATCH] feat(hadis): support global arguments filtering and expand search with books and narrators --- apps/hadis/views/hadis.py | 96 +++++++++++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 23 deletions(-) diff --git a/apps/hadis/views/hadis.py b/apps/hadis/views/hadis.py index 9233c9f..f19d920 100644 --- a/apps/hadis/views/hadis.py +++ b/apps/hadis/views/hadis.py @@ -366,6 +366,9 @@ class HadisListView(ListAPIView): search_conditions |= Q(title_narrator__icontains=search_query) search_conditions |= Q(translation__icontains=search_query) search_conditions |= Q(tags__title__icontains=search_query) + search_conditions |= Q(references__book_reference__title__icontains=search_query) + search_conditions |= Q(transmitters__transmitter__full_name__icontains=search_query) + search_conditions |= Q(transmitters__transmitter__known_as__icontains=search_query) queryset = queryset.filter(search_conditions) # 👇 2. Apply Status Filter (Supports multiple comma-separated slugs) @@ -448,7 +451,7 @@ class HadisListView(ListAPIView): class HadisMainListView(ListAPIView): """ - API view to list Hadis by category_id + API view to list Hadis globally with comprehensive filtering """ serializer_class = HadisListSerializer authentication_classes = [TokenAuthentication] @@ -460,7 +463,12 @@ class HadisMainListView(ListAPIView): return self.list(request, *args, **kwargs) def get_queryset(self): - queryset = Hadis.objects.select_related('category__sect', 'hadis_status').prefetch_related( + queryset = Hadis.objects.filter(status=True).select_related( + 'category__sect', 'hadis_status' + ).annotate( + layer_count=Count('transmitters__narrator_layer', distinct=True) + ).prefetch_related( + 'tags', Prefetch( 'references', queryset=HadisReference.objects.select_related( @@ -478,21 +486,58 @@ class HadisMainListView(ListAPIView): # Get search parameters search_query = self.request.query_params.get('search', None) + sect_filter = self.request.query_params.get('sect', None) or self.request.query_params.get('sect_type', None) + source_type_filter = self.request.query_params.get('source_type', None) status_filter = self.request.query_params.get('status', None) - category_filter = self.request.query_params.get('category', None) + author_filter = self.request.query_params.get('author', None) or self.request.query_params.get('source_author', None) + source_filter = self.request.query_params.get('source', None) + category_filter = self.request.query_params.get('category', None) or self.request.query_params.get('category_slug', None) tag_filter = self.request.query_params.get('tag', None) or self.request.query_params.get('tags', None) # Apply search filter if search_query: queryset = self.apply_search_filter(queryset, search_query) - # Apply status filter + # Apply sect filter (e.g. shia, sunni) + if sect_filter: + sects = [s.strip().lower() for s in sect_filter.split(',') if s.strip()] + if sects: + queryset = queryset.filter(category__sect__sect_type__in=sects) + + # Apply source_type filter (e.g. hadith, quran, history, fatwa, quote) + if source_type_filter: + source_types = [st.strip().lower() for st in source_type_filter.split(',') if st.strip()] + if source_types: + queryset = queryset.filter(category__source_type__in=source_types) + + # Apply status filter (by slug or title) if status_filter: - queryset = queryset.filter(hadis_status__title__icontains=status_filter) + status_items = [s.strip() for s in status_filter.split(',') if s.strip()] + queryset = queryset.filter( + Q(hadis_status__slug__in=status_items) | Q(hadis_status__title__icontains=status_filter) + ) + + # Apply author filter + if author_filter: + author_slugs = [a.strip() for a in author_filter.split(',') if a.strip()] + author_q = Q(references__book_reference__author__slug__in=author_slugs) + numeric_ids = [int(a) for a in author_slugs if a.isdigit()] + if numeric_ids: + author_q |= Q(references__book_reference__author_id__in=numeric_ids) + queryset = queryset.filter(author_q) + + # Apply source filter (reference books) + if source_filter: + source_slugs = [s.strip() for s in source_filter.split(',') if s.strip()] + if source_slugs: + queryset = queryset.filter(references__book_reference__slug__in=source_slugs) # Apply category filter if category_filter: - queryset = queryset.filter(category__title__icontains=category_filter) + cat_items = [c.strip() for c in category_filter.split(',') if c.strip()] + queryset = queryset.filter( + Q(category__slug__in=cat_items) | Q(category__title__icontains=category_filter) + ) # Apply tag filter if tag_filter: @@ -518,7 +563,23 @@ class HadisMainListView(ListAPIView): ).values_list('content_id', flat=True) queryset = queryset.filter(id__in=bookmarked_ids) - return queryset + return queryset.distinct() + + def get_serializer_context(self): + """Add user bookmarks to serializer context to avoid caching issues""" + context = super().get_serializer_context() + user = self.request.user + if user.is_authenticated: + from apps.bookmark.models.bookmark import Bookmark + user_bookmarks = Bookmark.objects.filter( + user=user, + service=Bookmark.ServiceChoices.HADITH, + status=True + ).values_list('content_id', flat=True) + context['user_bookmarked_hadis_ids'] = set(user_bookmarks) + else: + context['user_bookmarked_hadis_ids'] = set() + return context def list(self, request, *args, **kwargs): queryset = self.get_queryset() @@ -528,19 +589,7 @@ class HadisMainListView(ListAPIView): if page is not None: serializer = self.get_serializer(page, many=True) paginated_response = self.get_paginated_response(serializer.data) - - # Get category titles - category_titles = self.get_category_titles(request) - - # Get status titles - status_titles = self.get_status_titles(request) - - # Modify the paginated response to include our custom data - response_data = paginated_response.data - # response_data['category_titles'] = self.get_cached_category_titles(request) - # response_data['status_titles'] = self.get_cached_status_titles(request) - - return Response(response_data) + return Response(paginated_response.data) # Fallback for when pagination is disabled serializer = self.get_serializer(queryset, many=True) @@ -549,8 +598,6 @@ class HadisMainListView(ListAPIView): 'results': serializer.data }) - return Response(response_data) - def get_category_titles(self,request): """Get list of category titles based on language""" from ..models import HadisCategory @@ -579,7 +626,7 @@ class HadisMainListView(ListAPIView): def apply_search_filter(self, queryset, search_query): """ Apply search filter across multiple fields including JSONFields. - Searches in: title, title_narrator, text, translation + Searches in: title, title_narrator, text, translation, tags, book references, transmitters """ from django.db.models import Q @@ -592,6 +639,9 @@ class HadisMainListView(ListAPIView): search_conditions |= Q(title_narrator__icontains=search_query) search_conditions |= Q(translation__icontains=search_query) search_conditions |= Q(tags__title__icontains=search_query) + search_conditions |= Q(references__book_reference__title__icontains=search_query) + search_conditions |= Q(transmitters__transmitter__full_name__icontains=search_query) + search_conditions |= Q(transmitters__transmitter__known_as__icontains=search_query) return queryset.filter(search_conditions)