You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

905 lines
35 KiB

from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.generics import ListAPIView, RetrieveAPIView
from django.shortcuts import get_object_or_404
from utils.pagination import NoPagination, StandardResultsSetPagination
from rest_framework.pagination import PageNumberPagination
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 ..serializers import HadisListSerializer, HadisBasicSerializer, HadisDetailSerializer, HadisCollectionListSerializer, HadisSyncSerializer,HadisCorrectionSerializer,HadisTransmitterListSerializer , SimpleCategory, NarratorLayerSerializer , PinnedHadisCollectionSerializer
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
from ..serializers.category import get_localized_text
from rest_framework import status
class PinnedHadisCollectionListView(ListAPIView):
"""
API view to list pinned hadis collections with metadata info
"""
serializer_class = PinnedHadisCollectionSerializer
pagination_class = NoPagination
permission_classes = [IsAuthenticated]
authentication_classes = [TokenAuthentication]
@hadis_collections_swagger
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def get_queryset(self):
return HadisCollection.objects.filter(
status=True,
pin_top=True
).order_by('order', '-created_at')
def list(self, request, *args, **kwargs):
# 1. گرفتن خروجی استاندارد لیست مجموعه‌ها
response = super().list(request, *args, **kwargs)
# 2. محاسبه آمار (Info)
# تعداد کتگوری‌های فعال
categories_count = HadisCategory.objects.count()
# تعداد نریتورها (راویان)
narrators_count = Transmitters.objects.count()
# تعداد سورس‌ها (کتاب‌های منبع)
# فرض بر این است که BookReference مدل منابع شماست
from apps.hadis.models import BookReference
sources_count = BookReference.objects.count()
# تعداد بوکمارک‌های کاربر در سرویس حدیث
from apps.bookmark.models.bookmark import Bookmark
bookmarks_count = Bookmark.objects.filter(
user=request.user,
service=Bookmark.ServiceChoices.HADITH,
status=True
).count()
# 3. ساختن دیکشنری info
info = {
"categories_count": categories_count,
# "bookmarks_count": bookmarks_count,
"narrators_count": narrators_count,
"sources_count": sources_count,
}
# 4. بازسازی دیتای نهایی مشابه اپ ویدیو
custom_data = {
"count": len(response.data) if isinstance(response.data, list) else response.data.get("count"),
"next": response.data.get("next") if isinstance(response.data, dict) else None,
"previous": response.data.get("previous") if isinstance(response.data, dict) else None,
"info": info,
"results": response.data if isinstance(response.data, list) else response.data.get("results")
}
return Response(custom_data, status=status.HTTP_200_OK)
class HadisCollectionListView(ListAPIView):
"""
API view to list all hadis collections
"""
queryset = HadisCollection.objects.filter(status=True).order_by('order')
serializer_class = HadisCollectionListSerializer
pagination_class = NoPagination
@hadis_collections_swagger
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
class HadisSyncView(ListAPIView):
"""
API view to sync all hadis data for offline mode
"""
serializer_class = HadisSyncSerializer
pagination_class = NoPagination
def get_queryset(self):
return (
Hadis.objects
.filter(status=True)
.select_related('category', 'hadis_status')
.prefetch_related(
'tags',
Prefetch(
'transmitters',
queryset=HadisTransmitter.objects.select_related(
'transmitter',
'transmitter__reliability',
'narrator_layer'
).order_by('order')
),
Prefetch(
'references',
queryset=HadisReference.objects
.select_related('book_reference')
.prefetch_related(
'book_reference__authors',
Prefetch(
'images',
queryset=ReferenceImage.objects.order_by('priority')
)
)
),
# 👇 اضافه شدن واکشی عمیق برای تصحیحات
Prefetch(
'hadiscorrection_set',
queryset=HadisCorrection.objects.prefetch_related(
'references__book_reference__authors',
'references__edition',
'references__book_volume',
'references__images'
)
),
# 👇 اضافه شدن واکشی عمیق برای تفاسیر
Prefetch(
'category__interpretations',
queryset=HadisInterpretation.objects.prefetch_related(
'references__book_reference__authors',
'references__edition',
'references__book_volume',
'references__images'
)
),
)
.order_by('id')
)
@hadis_sync_swagger
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def list(self, request, *args, **kwargs):
queryset = self.get_queryset()
serializer = self.get_serializer(
queryset,
many=True,
context={'request': request},
)
return Response({
'count': len(serializer.data),
'results': serializer.data
})
from ..serializers.category import CategoryQuranVerseSerializer
class HadisListView(ListAPIView):
"""
API view to list Hadis by category_id
"""
serializer_class = HadisListSerializer
pagination_class = StandardResultsSetPagination
authentication_classes = [TokenAuthentication]
@hadis_list_swagger
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def list(self, request, *args, **kwargs):
# 1. Run the standard list logic (get pagination, filter, results)
response = super().list(request, *args, **kwargs)
# 2. Find the "Parent" Category based on the URL slug
category_slug = self.kwargs.get('category_slug')
category_obj = get_object_or_404(HadisCategory, slug=category_slug)
# 3. Serialize this single category for the Hero section
category_data = SimpleCategory(category_obj).data
# 4. Check if it's a Quran Category and fetch verse data
quran_data = None
if category_obj.source_type == HadisCategory.SourceType.QURAN:
try:
from ..models.category import CategoryQuranVerse
verse_obj = CategoryQuranVerse.objects.prefetch_related(
'secondary_translations'
).get(category=category_obj)
from ..serializers.category import CategoryQuranVerseSerializer
quran_data = CategoryQuranVerseSerializer(
verse_obj,
context={'request': request}
).data
except CategoryQuranVerse.DoesNotExist:
pass
# 👇 5. NEW LOGIC: Fetch all statuses for frontend filtering UI
lang = getattr(request, "LANGUAGE_CODE", None) or "fa"
statuses_list = [
{
"id": status.id,
"slug": status.slug,
"title": get_localized_text(status.title, request=request, language_code=lang) or "",
"color": status.color,
"main_color_code": status.main_color_code,
}
for status in HadisStatus.objects.all().order_by('order')
]
# 6. Inject it into the response data
if isinstance(response.data, dict):
ordered_data = {}
for key in ['count', 'next', 'previous']:
if key in response.data:
ordered_data[key] = response.data[key]
ordered_data['current_category'] = category_data
ordered_data['quran_data'] = quran_data
# 👇 تزریق لیست استاتوس‌ها قبل از نتایج
ordered_data['statuses'] = statuses_list
if 'results' in response.data:
ordered_data['results'] = response.data['results']
response.data = ordered_data
return response
def get_queryset(self):
category_slug = self.kwargs.get('category_slug')
if not HadisCategory.objects.filter(slug=category_slug).exists():
return Hadis.objects.none()
queryset = Hadis.objects.filter(
category__slug=category_slug,
status=True
).annotate(
layer_count=Count('transmitters__narrator_layer', distinct=True)
).select_related('category')
# 👇 1. Apply Search Filter
search_query = self.request.query_params.get('search', None)
if search_query:
search_conditions = Q(text__icontains=search_query)
search_conditions |= Q(title__icontains=search_query)
search_conditions |= Q(title_narrator__icontains=search_query)
search_conditions |= Q(translation__icontains=search_query)
queryset = queryset.filter(search_conditions)
# 👇 2. Apply Status Filter (Supports multiple comma-separated slugs)
status_filter = self.request.query_params.get('status', None)
if status_filter:
status_slugs = [s.strip() for s in status_filter.split(',')]
queryset = queryset.filter(hadis_status__slug__in=status_slugs)
# 👇 3. Apply Transmitter Filter (Supports multiple comma-separated slugs)
transmitter_filter = self.request.query_params.get('transmitter', None)
if transmitter_filter:
transmitter_slugs = [t.strip() for t in transmitter_filter.split(',')]
queryset = queryset.filter(transmitters__transmitter__slug__in=transmitter_slugs)
# 👇 4. Apply Source Filter (Supports multiple comma-separated slugs)
source_filter = self.request.query_params.get('source', None)
if source_filter:
source_slugs = [s.strip() for s in source_filter.split(',')]
queryset = queryset.filter(references__book_reference__slug__in=source_slugs)
# Filter by bookmarks if provided
is_bookmark = self.request.query_params.get('is_bookmark', '').lower()
if is_bookmark == 'true' and self.request.user.is_authenticated:
from apps.bookmark.models.bookmark import Bookmark
bookmarked_ids = Bookmark.objects.filter(
user=self.request.user,
service=Bookmark.ServiceChoices.HADITH,
status=True
).values_list('content_id', flat=True)
queryset = queryset.filter(id__in=bookmarked_ids)
# ❗️ distinct() برای جلوگیری از دابلیکیت شدن احادیث به خاطر ManyToMany ها الزامی است
return queryset.distinct()
def get_serializer_context(self):
"""Add user bookmarks to serializer context to avoid caching issues"""
context = super().get_serializer_context()
# Add user's bookmarked hadis IDs to 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
class HadisMainListView(ListAPIView):
"""
API view to list Hadis by category_id
"""
serializer_class = HadisListSerializer
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
pagination_class = StandardResultsSetPagination
@hadis_main_list_swagger
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def get_queryset(self):
# queryset = Hadis.objects.select_related('category', 'hadis_status')
queryset = Hadis.objects.select_related('category__sect', 'hadis_status')
# Get search parameters
search_query = self.request.query_params.get('search', None)
status_filter = self.request.query_params.get('status', None)
category_filter = self.request.query_params.get('category', None)
# Apply search filter
if search_query:
queryset = self.apply_search_filter(queryset, search_query)
# Apply status filter
if status_filter:
queryset = queryset.filter(hadis_status__title__icontains=status_filter)
# Apply category filter
if category_filter:
queryset = queryset.filter(category__title__icontains=category_filter)
# Filter by bookmarks if provided
is_bookmark = self.request.query_params.get('is_bookmark', '').lower()
if is_bookmark == 'true' and self.request.user.is_authenticated:
from apps.bookmark.models.bookmark import Bookmark
bookmarked_ids = Bookmark.objects.filter(
user=self.request.user,
service=Bookmark.ServiceChoices.HADITH,
status=True
).values_list('content_id', flat=True)
queryset = queryset.filter(id__in=bookmarked_ids)
return queryset
def list(self, request, *args, **kwargs):
queryset = self.get_queryset()
# Apply pagination
page = self.paginate_queryset(queryset)
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)
# Fallback for when pagination is disabled
serializer = self.get_serializer(queryset, many=True)
return Response({
'count': queryset.count(),
'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
categories = HadisCategory.objects.all()
category_titles = []
for category in categories:
title = get_localized_text(category.title,request)
category_titles.append(title)
return category_titles
def get_status_titles(self, request):
"""Get list of status titles based on language"""
from ..models import HadisStatus
statuses = HadisStatus.objects.all().order_by('order')
status_titles = []
for status in statuses:
title = get_localized_text(status.title,request)
status_titles.append(title)
return status_titles
def apply_search_filter(self, queryset, search_query):
"""
Apply search filter across multiple fields including JSONFields.
Searches in: title, title_narrator, text, translation
"""
from django.db.models import Q
# Basic search conditions
search_conditions = Q(text__icontains=search_query)
# For JSONFields, search in the JSON string representation
# This will find matches in the "text" values within the JSON arrays
search_conditions |= Q(title__icontains=search_query)
search_conditions |= Q(title_narrator__icontains=search_query)
search_conditions |= Q(translation__icontains=search_query)
return queryset.filter(search_conditions)
#we add this later
# def get_cached_category_titles(self, request):
# """Fetches categories, cached for 1 hour to reduce DB load"""
# lang = getattr(request, "LANGUAGE_CODE", "en")
# cache_key = f"hadis_meta_categories_{lang}"
# data = cache.get(cache_key)
# if not data:
# # If not in cache, fetch from DB
# from ..models import HadisCategory
# categories = HadisCategory.objects.all().only('id', 'title') # Fetch only needed fields
# # Build list
# data = [get_localized_text(c.title, request) for c in categories]
# # Save to cache
# cache.set(cache_key, data, timeout=60 * 60) # 1 Hour
# return data
# def get_cached_status_titles(self, request):
# """Fetches statuses, cached for 1 hour"""
# lang = getattr(request, "LANGUAGE_CODE", "en")
# cache_key = f"hadis_meta_statuses_{lang}"
# data = cache.get(cache_key)
# if not data:
# from ..models import HadisStatus
# statuses = HadisStatus.objects.all().order_by('order')
# data = [get_localized_text(s.title, request) for s in statuses]
# cache.set(cache_key, data, timeout=60 * 60)
# return data
class HadisBasicView(RetrieveAPIView):
"""
API view to retrieve basic Hadis information by hadis_slug
"""
serializer_class = HadisBasicSerializer
lookup_field = 'slug'
lookup_url_kwarg = 'hadis_slug'
authentication_classes = [TokenAuthentication]
@hadis_basic_swagger
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def get_queryset(self):
return Hadis.objects.filter(status=True)
def get_serializer_context(self):
"""Add user bookmarks to serializer context to avoid caching issues"""
context = super().get_serializer_context()
# Add user's bookmarked hadis IDs to 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
class HadisDetailView(RetrieveAPIView):
"""
API view to retrieve detailed Hadis information by hadis_slug (excluding transmitters and corrections)
"""
serializer_class = HadisDetailSerializer
lookup_field = 'slug'
lookup_url_kwarg = 'hadis_slug'
@hadis_detail_swagger
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def get_queryset(self):
return Hadis.objects.filter(status=True).select_related(
'category', 'hadis_status'
).prefetch_related(
'tags',
'references__book_reference',
'references__book_reference__authors',
'references__images',
)
class HadisTransmittersView(RetrieveAPIView):
"""
Fetches a single Hadis but filters the nested Transmitters list
if a ?layer=slug param is provided.
"""
serializer_class = HadisTransmitterListSerializer
lookup_field = 'slug'
lookup_url_kwarg = 'hadis_slug'
pagination_class = NoPagination
@hadis_transmitters_swagger
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def get_queryset(self):
# 1. Get the filter param
layer_slug = self.request.query_params.get('layer')
# 2. Build the query for the "Child" (Transmitters)
# We start with the base optimization (select_related)
transmitter_qs = HadisTransmitter.objects.select_related(
'transmitter',
'narrator_layer'
).order_by('order')
# 3. Apply the filter to the Child Query (if param exists)
if layer_slug:
# Assumes 'NarratorLayer' has a 'slug' field.
# If not, use 'narrator_layer__name' or 'narrator_layer__id'.
transmitter_qs = transmitter_qs.filter(narrator_layer__slug=layer_slug)
# 4. Use the Prefetch object to inject this filtered list into the Parent
return Hadis.objects.filter(status=True).prefetch_related(
Prefetch('transmitters', queryset=transmitter_qs)
)
class HadisCorrectionsView(ListAPIView):
"""
API view to retrieve corrections for a specific hadis
"""
serializer_class = HadisCorrectionSerializer
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
pagination_class = StandardResultsSetPagination
lookup_field = 'slug'
lookup_url_kwarg = 'hadis_slug'
@hadis_corrections_swagger
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
# hadis = self.get_object()
# corrections_data = []
# for correction in hadis.hadiscorrection_set.all():
# correction_info = {
# 'id': correction.id,
# 'title': correction.title,
# 'description': correction.description,
# 'translation': correction.translation
# }
# corrections_data.append(correction_info)
# return Response({
# 'hadis_id': hadis.id,
# 'corrections_count': len(corrections_data),
# 'corrections': corrections_data
# })
def get_queryset(self):
hadis_slug = self.kwargs.get('hadis_slug')
try:
hadis = Hadis.objects.get(slug=hadis_slug, status=True)
if not HadisCorrection.objects.filter(hadis=hadis).exists():
return Hadis.objects.none()
queryset = HadisCorrection.objects.filter(hadis=hadis)
# Filter by bookmarks if provided
is_bookmark = self.request.query_params.get('is_bookmark', '').lower()
if is_bookmark == 'true' and self.request.user.is_authenticated:
from apps.bookmark.models.bookmark import Bookmark
bookmarked_ids = Bookmark.objects.filter(
user=self.request.user,
service=Bookmark.ServiceChoices.HADITH_CORRECTION,
status=True
).values_list('content_id', flat=True)
queryset = queryset.filter(id__in=bookmarked_ids)
return queryset
except Hadis.DoesNotExist:
return HadisCorrection.objects.none()
class HadisLayersView(ListAPIView):
"""
API view to retrieve all narrator layers for a specific hadis
"""
serializer_class = NarratorLayerSerializer
pagination_class = NoPagination
lookup_field = 'slug'
lookup_url_kwarg = 'hadis_slug'
@hadis_layers_swagger
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def get_queryset(self):
from ..models import NarratorLayer
hadis_slug = self.kwargs.get('hadis_slug')
# Get the hadis object to ensure it exists
hadis = get_object_or_404(Hadis, slug=hadis_slug, status=True)
# Get all distinct narrator layer IDs for this hadis
layer_ids = HadisTransmitter.objects.filter(
hadis=hadis
).values_list('narrator_layer', flat=True).distinct()
# Filter out None values (transmitters without layers)
layer_ids = [lid for lid in layer_ids if lid is not None]
# Return the layer objects ordered by number
return NarratorLayer.objects.filter(id__in=layer_ids).order_by('number')
class HadisFiltersView(ListAPIView):
"""
API view to return filter data for hadis
Returns statuses and categories for filtering
"""
pagination_class = NoPagination
@arguments_filters_swagger
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def get_queryset(self):
# This view doesn't need a queryset, it returns computed data
return Hadis.objects.none()
def list(self, request, *args, **kwargs):
# Get statuses from HadisStatus model
statuses = []
for status in HadisStatus.objects.all().order_by('order'):
title_text = get_localized_text(status.title, request)
if title_text and status.slug:
statuses.append({
'text': title_text,
'slug': status.slug
})
# Get categories from HadisCategory model
categories = []
for category in HadisCategory.objects.all().order_by('order'):
title_text = get_localized_text(category.title, request)
if title_text and category.slug:
categories.append({
'text': title_text,
'slug': category.slug
})
response_data = {
'statuses': statuses,
'categories': categories
}
return Response(response_data)
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import AllowAny
from rest_framework.authentication import TokenAuthentication
from drf_yasg.utils import swagger_auto_schema
from django.utils.translation import get_language
from apps.hadis.models import HadisStatus, HadisCategory, BookReference, Transmitters
from drf_yasg import openapi
class HadisFiltersSyncAPIView(APIView):
"""
API view to return all filter data for hadis (Sync API for Mobile App)
Returns statuses, categories, sources (books), and narrators.
Optimized for offline client-side searching and sorting.
"""
permission_classes = [AllowAny]
authentication_classes = [TokenAuthentication]
@swagger_auto_schema(
operation_description=(
"Sync API for Flutter/mobile clients: returns flat hadis filter data for offline "
"searching and local filtering. Titles are localized from multilingual fields based "
"on the active request language."
),
operation_summary="Sync Hadis Filters",
tags=['Dobodbi - Hadis'],
manual_parameters=[
openapi.Parameter(
'Accept-Language',
openapi.IN_HEADER,
description="Language code for localized titles, for example `en`, `fa`, `ar`, `ur`, or `ru`.",
type=openapi.TYPE_STRING,
required=False,
)
],
responses={
status.HTTP_200_OK: openapi.Response(
description="Flat filter payload for hadis categories, statuses, sources, and narrators.",
examples={
"application/json": {
"statuses": [
{
"id": 133,
"slug": "sahih",
"title": "Authentic",
"order": 1,
"color": "green",
"main_color_code": "#1DAC43"
}
],
"categories": [
{
"id": 1036,
"slug": "general-hadiths",
"title": "General Hadiths",
"order": 1
}
],
"sources": [
{
"id": 2538,
"slug": "man-la-yahduruhu-al-faqih",
"title": "Man La Yahduruhu al-Faqih",
"search_keywords": "Shaykh al-Saduq"
}
],
"narrators": [
{
"id": 501,
"slug": "muhammad-ibn-muslim",
"title": "Muhammad ibn Muslim",
"search_aliases": "Abu Ja'far"
}
]
}
}
)
}
)
def get(self, request, *args, **kwargs):
# تشخیص زبان برای ارسال دیتای لوکالایز شده
lang = getattr(request, "LANGUAGE_CODE", None) or get_language() or "ru"
# ۱. استتوس‌ها (مرتب‌شده بر اساس فیلد order)
statuses = [
{
"id": status.id,
"slug": status.slug,
"title": get_localized_text(status.title, request=request, language_code=lang) or "",
"order": status.order,
"color": status.color,
"main_color_code": status.main_color_code,
}
for status in HadisStatus.objects.all().order_by('order')
]
# ۲. دسته‌بندی‌ها (مرتب‌شده بر اساس فیلد order)
categories = [
{
"id": cat.id,
"slug": cat.slug,
"title": get_localized_text(cat.title, request=request, language_code=lang) or "",
"order": cat.order,
}
for cat in HadisCategory.objects.all().order_by('order')
]
# ۳. منابع / کتاب‌ها
sources = []
# استفاده از prefetch_related برای جلوگیری از مشکل N+1 Query هنگام گرفتن نویسنده‌ها
books = BookReference.objects.prefetch_related('authors').all().order_by('order', '-id')
for book in books:
# استخراج نام نویسنده‌ها برای سرچ آفلاین فلاتر
authors_names = [
get_localized_text(author.name, request=request, language_code=lang) or ""
for author in book.authors.all()
]
sources.append({
"id": book.id,
"slug": book.slug,
"title": get_localized_text(book.title, request=request, language_code=lang) or "",
# تمام اسم‌های نویسنده‌ها را به هم می‌چسبانیم تا فلاتر بتواند روی آن‌ها سرچ بزند
# "search_keywords": " ".join(authors_names).strip()
})
# ۴. راویان (نریتورها)
narrators = []
transmitters = Transmitters.objects.all().order_by('id')
for narrator in transmitters:
# گرفتن نام‌های مستعار
known_as = get_localized_text(narrator.known_as, request=request, language_code=lang) or ""
nickname = get_localized_text(narrator.nickname, request=request, language_code=lang) or ""
kunya = get_localized_text(narrator.kunya, request=request, language_code=lang) or ""
# تجمیع نام‌های جایگزین
search_aliases = f"{known_as} {nickname} {kunya}".strip()
narrators.append({
"id": narrator.id,
"slug": narrator.slug,
"title": get_localized_text(narrator.full_name, request=request, language_code=lang) or "",
# فلاتر روی این فیلد + title سرچ می‌زند
# "search_aliases": search_aliases
})
# خروجی نهایی بدون پجینیشن
return Response({
"statuses": statuses,
"categories": categories,
"sources": sources,
"narrators": narrators
})
from ..serializers.hadis import HadisCorrectionDetailSerializer, HadisInterpretationDetailSerializer
from ..models import HadisInterpretation
class HadisCorrectionDetailView(RetrieveAPIView):
"""API View to get full details of a specific HadisCorrection by slug"""
serializer_class = HadisCorrectionDetailSerializer
lookup_field = 'slug'
lookup_url_kwarg = 'correction_slug'
permission_classes = [AllowAny]
def get_queryset(self):
return HadisCorrection.objects.all().select_related(
'hadis'
).prefetch_related(
'references__book_reference__authors',
'references__edition',
'references__book_volume',
'references__images'
)
class HadisInterpretationDetailView(RetrieveAPIView):
"""API View to get full details of a specific HadisInterpretation by slug"""
serializer_class = HadisInterpretationDetailSerializer
lookup_field = 'slug' # 👈 تغییر به slug
lookup_url_kwarg = 'interpretation_slug'
permission_classes = [AllowAny]
def get_queryset(self):
return HadisInterpretation.objects.all().select_related(
'category'
).prefetch_related(
'references__book_reference__authors',
'references__edition',
'references__book_volume',
'references__images'
)
# 👈 هندل کردن حالت امن برای زمانی که چند تفسیر در یک کتگوری باشند
def get_object(self):
queryset = self.filter_queryset(self.get_queryset())
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
obj = queryset.filter(**filter_kwargs).first() # گرفتن اولین آبجکت
if not obj:
raise Http404("No HadisInterpretation matches the given query.")
self.check_object_permissions(self.request, obj)
return obj