@ -10,7 +10,7 @@ from rest_framework.response import Response
from django.db.models import Count
from django.db.models import Prefetch
from ..serializers.category import get_localized_text
from ..models import Transmitters , HadisCategory , Hadis , HadisCollection , HadisTransmitter , HadisCorrection , HadisReference , HadisStatus , ReferenceImage
from ..models import Transmitters , HadisCategory , Hadis , HadisCollection , HadisTransmitter , HadisCorrection , HadisReference , HadisStatus , ReferenceImage , CorrectionReference , HadisInterpretation , InterpretationReference
from ..serializers import HadisListSerializer , HadisBasicSerializer , HadisDetailSerializer , HadisCollectionListSerializer , HadisSyncSerializer , HadisCorrectionSerializer , HadisTransmitterListSerializer , SimpleCategory , NarratorLayerSerializer , PinnedHadisCollectionSerializer , HadisSourceDetailsSerializer
from ..docs import arguments_filters_swagger , hadis_list_swagger , hadis_detail_swagger , hadis_collections_swagger , hadis_sync_swagger , hadis_transmitters_swagger , hadis_corrections_swagger , hadis_basic_swagger , hadis_main_list_swagger , hadis_layers_swagger
from django.db.models import Q
@ -33,10 +33,17 @@ class PinnedHadisCollectionListView(ListAPIView):
return self . list ( request , * args , * * kwargs )
def get_queryset ( self ) :
from django.db.models import Prefetch
from apps.hadis.models import HadisInCollection
return HadisCollection . objects . filter (
status = True ,
display_position = HadisCollection . DisplayPosition . PINNED ,
hadis_items__hadis__status = True
) . prefetch_related (
Prefetch (
' hadis_items ' ,
queryset = HadisInCollection . objects . filter ( hadis__status = True ) . order_by ( ' order ' )
)
) . distinct ( ) . order_by ( ' order ' , ' -created_at ' )
def list ( self , request , * args , * * kwargs ) :
@ -100,9 +107,16 @@ class HadisCollectionListView(ListAPIView):
return self . list ( request , * args , * * kwargs )
def get_queryset ( self ) :
from django.db.models import Prefetch
from apps.hadis.models import HadisInCollection
return HadisCollection . objects . filter (
status = True ,
display_position = HadisCollection . DisplayPosition . MIDDLE
) . prefetch_related (
Prefetch (
' hadis_items ' ,
queryset = HadisInCollection . objects . filter ( hadis__status = True ) . order_by ( ' order ' )
)
) . order_by ( ' order ' , ' -created_at ' )
@ -143,24 +157,44 @@ class HadisSyncView(ListAPIView):
)
)
) ,
# 👇 اضافه شدن واکشی عمیق برای تصحیحات
# 👇 اضافه شدن واکشی عمیق برای تصحیحات (بدون N+1)
Prefetch (
' hadiscorrection_set ' ,
queryset = HadisCorrection . objects . select_related ( ' hadis ' ) . prefetch_related (
' references__book_reference__author ' ,
' references__edition ' ,
' references__book_volume ' ,
' references__images '
Prefetch (
' references ' ,
queryset = CorrectionReference . objects . select_related (
' book_reference ' ,
' book_reference__author ' ,
' edition ' ,
' book_volume ' ,
' book_volume__edition '
) . prefetch_related (
' images ' ,
' book_reference__editions ' ,
' book_reference__volumes '
)
)
)
) ,
# 👇 اضافه شدن واکشی عمیق برای تفاسیر
# 👇 اضافه شدن واکشی عمیق برای تفاسیر (بدون N+1)
Prefetch (
' category__interpretations ' ,
queryset = HadisInterpretation . objects . select_related ( ' category ' ) . prefetch_related (
' references__book_reference__author ' ,
' references__edition ' ,
' references__book_volume ' ,
' references__images '
Prefetch (
' references ' ,
queryset = InterpretationReference . objects . select_related (
' book_reference ' ,
' book_reference__author ' ,
' edition ' ,
' book_volume ' ,
' book_volume__edition '
) . prefetch_related (
' images ' ,
' book_reference__editions ' ,
' book_reference__volumes '
)
)
)
) ,
)
@ -670,11 +704,13 @@ class CategoryHadisCorrectionsView(ListAPIView):
@swagger_auto_schema (
operation_summary = " Get Category Hadis Corrections " ,
operation_description = " Returns all text corrections across all hadiths belonging to a specific category, including hadis_info for each correction. " ,
operation_description = " Returns all text corrections across all hadiths belonging to a specific category, including hadis_info for each correction. Supports filtering by multiple hadiths and/or categories. " ,
tags = [ ' Dobodbi - Hadis (V2) ' ] ,
manual_parameters = [
openapi . Parameter ( ' search ' , openapi . IN_QUERY , description = " Search in text or narrator " , type = openapi . TYPE_STRING ) ,
openapi . Parameter ( ' hadis_slug ' , openapi . IN_QUERY , description = " Filter corrections by specific hadith slug " , type = openapi . TYPE_STRING ) ,
openapi . Parameter ( ' hadis_slug ' , openapi . IN_QUERY , description = " Filter corrections by specific hadith slug (comma-separated for multiple) " , type = openapi . TYPE_STRING ) ,
openapi . Parameter ( ' hadis_slugs ' , openapi . IN_QUERY , description = " Filter corrections by multiple hadith slugs (comma-separated) " , type = openapi . TYPE_STRING ) ,
openapi . Parameter ( ' category_slugs ' , openapi . IN_QUERY , description = " Filter corrections by multiple category slugs (comma-separated) " , type = openapi . TYPE_STRING ) ,
openapi . Parameter ( ' is_bookmark ' , openapi . IN_QUERY , description = " Filter only bookmarked corrections (requires auth) " , type = openapi . TYPE_BOOLEAN ) ,
]
)
@ -684,12 +720,52 @@ class CategoryHadisCorrectionsView(ListAPIView):
def get_queryset ( self ) :
category_slug = self . kwargs . get ( ' category_slug ' )
try :
category = HadisCategory . objects . get ( slug = category_slug )
category_ids = category . get_descendants ( include_self = True ) . values_list ( ' id ' , flat = True )
hadis_ids = Hadis . objects . filter (
category_id__in = category_ids ,
status = True
) . values_list ( ' id ' , flat = True )
category_ids = [ ]
if category_slug :
category = HadisCategory . objects . get ( slug = category_slug )
category_ids = list ( category . get_descendants ( include_self = True ) . values_list ( ' id ' , flat = True ) )
# Optional filter by multiple categories (selected subcategories or category slugs/IDs)
cat_param_keys = [ ' category_slugs ' , ' categories ' , ' category_slug ' ]
cat_slug_list = [ ]
for key in cat_param_keys :
vals = self . request . query_params . getlist ( key ) if hasattr ( self . request . query_params , ' getlist ' ) else [ ]
if not vals and key in self . request . query_params :
val = self . request . query_params . get ( key )
if val :
vals = [ val ] if isinstance ( val , str ) else list ( val )
for v in vals :
if isinstance ( v , str ) :
cat_slug_list . extend ( [ s . strip ( ) for s in v . split ( ' , ' ) if s . strip ( ) ] )
elif isinstance ( v , ( list , tuple ) ) :
cat_slug_list . extend ( v )
elif v is not None :
cat_slug_list . append ( str ( v ) )
if cat_slug_list :
cat_filter = Q ( slug__in = cat_slug_list )
id_list = [ int ( s ) for s in cat_slug_list if str ( s ) . isdigit ( ) ]
if id_list :
cat_filter | = Q ( id__in = id_list )
selected_categories = HadisCategory . objects . filter ( cat_filter )
selected_cat_ids = set ( )
for sc in selected_categories :
selected_cat_ids . update ( sc . get_descendants ( include_self = True ) . values_list ( ' id ' , flat = True ) )
if selected_cat_ids :
if category_ids :
category_ids = list ( set ( category_ids ) . intersection ( selected_cat_ids ) )
else :
category_ids = list ( selected_cat_ids )
else :
return HadisCorrection . objects . none ( )
hadis_filter_kwargs = { ' status ' : True }
if category_ids :
hadis_filter_kwargs [ ' category_id__in ' ] = category_ids
hadis_ids = Hadis . objects . filter ( * * hadis_filter_kwargs ) . values_list ( ' id ' , flat = True )
queryset = HadisCorrection . objects . filter (
hadis_id__in = hadis_ids
@ -711,10 +787,30 @@ class CategoryHadisCorrectionsView(ListAPIView):
)
queryset = queryset . filter ( search_conditions )
# Optional filter by specific hadith slug within this category
hadis_slug = self . request . query_params . get ( ' hadis_slug ' , None )
if hadis_slug :
queryset = queryset . filter ( hadis__slug = hadis_slug )
# Optional filter by specific hadith slug(s) or ID(s)
hadis_param_keys = [ ' hadis_slug ' , ' hadis_slugs ' , ' hadiths ' ]
hadis_slug_list = [ ]
for key in hadis_param_keys :
vals = self . request . query_params . getlist ( key ) if hasattr ( self . request . query_params , ' getlist ' ) else [ ]
if not vals and key in self . request . query_params :
val = self . request . query_params . get ( key )
if val :
vals = [ val ] if isinstance ( val , str ) else list ( val )
for v in vals :
if isinstance ( v , str ) :
hadis_slug_list . extend ( [ s . strip ( ) for s in v . split ( ' , ' ) if s . strip ( ) ] )
elif isinstance ( v , ( list , tuple ) ) :
hadis_slug_list . extend ( v )
elif v is not None :
hadis_slug_list . append ( str ( v ) )
if hadis_slug_list :
hadis_query = Q ( hadis__slug__in = hadis_slug_list )
id_list = [ int ( s ) for s in hadis_slug_list if str ( s ) . isdigit ( ) ]
if id_list :
hadis_query | = Q ( hadis__id__in = id_list )
queryset = queryset . filter ( hadis_query )
# Filter by bookmarks if provided
is_bookmark = self . request . query_params . get ( ' is_bookmark ' , ' ' ) . lower ( )
@ -734,15 +830,44 @@ class CategoryHadisCorrectionsView(ListAPIView):
def list ( self , request , * args , * * kwargs ) :
response = super ( ) . list ( request , * args , * * kwargs )
category_slug = self . kwargs . get ( ' category_slug ' )
category_obj = HadisCategory . objects . filter ( slug = category_slug ) . first ( )
category_obj = HadisCategory . objects . filter ( slug = category_slug ) . first ( ) if category_slug else None
category_data = SimpleCategory ( category_obj , context = { ' request ' : request } ) . data if category_obj else None
available_hadiths = [ ]
available_categories = [ ]
if category_obj :
cat_ids = category_obj . get_descendants ( include_self = True ) . values_list ( ' id ' , flat = True )
hadiths_in_cat = Hadis . objects . filter (
category_id__in = cat_ids ,
status = True
) . order_by ( ' number ' , ' id ' )
for h in hadiths_in_cat :
h_title = get_localized_text ( h . title , request ) if hasattr ( h , ' title ' ) and h . title else f " Hadith {h.number} "
available_hadiths . append ( {
" id " : h . id ,
" number " : h . number ,
" slug " : h . slug ,
" title " : h_title ,
} )
subcats = category_obj . get_children ( ) if hasattr ( category_obj , ' get_children ' ) else category_obj . children . all ( )
for sc in subcats :
sc_title = get_localized_text ( sc . title , request ) if hasattr ( sc , ' title ' ) and sc . title else sc . slug
available_categories . append ( {
" id " : sc . id ,
" slug " : sc . slug ,
" title " : sc_title ,
} )
if isinstance ( response . data , dict ) :
ordered_data = {
' count ' : response . data . get ( ' count ' , 0 ) ,
' next ' : response . data . get ( ' next ' ) ,
' previous ' : response . data . get ( ' previous ' ) ,
' current_category ' : category_data ,
' available_hadiths ' : available_hadiths ,
' available_categories ' : available_categories ,
' results ' : response . data . get ( ' results ' , [ ] ) ,
}
response . data = ordered_data