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.
 
 
 
 
 

1836 lines
71 KiB

from rest_framework import serializers
import json
from django.utils.translation import gettext_lazy as _
from rest_framework.fields import SerializerMethodField
from urllib3 import request
from .category import LocalizedField, ArabicLocalizedField
from .category import get_localized_text, get_arabic_localized_text
from .category import get_localized_text
from apps.bookmark.serializers.bookmark import BookmarkStatusSerializer
from utils import absolute_https_url
from ..models import (
Hadis, HadisStatus, HadisTag, HadisTransmitter,
HadisReference, ReferenceImage, Transmitters, HadisCollection,
TransmitterOpinion, TransmitterOriginalText, BookReference, BookReferenceImage, BookAuthor, HadisCorrection,HadisInterpretation ,
CorrectionReferenceImage , InterpretationReferenceImage , OriginalTextReferenceImage,CorrectionReference,InterpretationReference,OriginalTextReference
)
def get_localized_correction_text(value, request=None):
"""
Resolve HadisCorrection.text in the same multilingual style used across the hadis app.
Supports:
- a parsed JSON list: [{"language_code": "en", "text": "..."}]
- a JSON string containing that list
- plain text fallback
"""
if value in (None, ""):
return value
parsed_value = value
if isinstance(value, str):
stripped = value.strip()
if stripped.startswith("[") or stripped.startswith("{"):
try:
parsed_value = json.loads(stripped)
except (TypeError, ValueError, json.JSONDecodeError):
return value
if isinstance(parsed_value, list):
return get_localized_text(parsed_value, request=request)
return value
class PinnedHadisCollectionSerializer(serializers.ModelSerializer):
title = LocalizedField()
summary = LocalizedField()
thumbnail = serializers.SerializerMethodField()
hadis_ids = serializers.SerializerMethodField()
class Meta:
model = HadisCollection
# فیلدهایی که برای نمایش در بخش پین شده (مثل اسلایدر یا لیست بالا) نیاز هست
fields = ['id', 'title', 'slug', 'summary', 'thumbnail', 'order', 'created_at', 'hadis_ids']
def get_thumbnail(self, obj):
if obj.thumbnail:
request = self.context.get('request')
if request:
return absolute_https_url(obj.thumbnail.url, request)
return absolute_https_url(obj.thumbnail.url)
return None
def get_hadis_ids(self, obj):
# دریافت شناسه احادیث فعال متعلق به این کالکشن به ترتیب
if hasattr(obj, '_prefetched_objects_cache') and 'hadis_items' in obj._prefetched_objects_cache:
items = [
item.hadis_id for item in obj.hadis_items.all()
if getattr(getattr(item, 'hadis', None), 'status', True)
]
return items
return list(
obj.hadis_items.filter(hadis__status=True)
.order_by('order')
.values_list('hadis_id', flat=True)
)
class HadisCollectionListSerializer(serializers.ModelSerializer):
thumbnail = serializers.SerializerMethodField()
title = LocalizedField()
summary = LocalizedField()
hadis_ids = serializers.SerializerMethodField()
class Meta:
model = HadisCollection
fields = ['id', 'title', 'summary', 'slug', 'thumbnail', 'hadis_ids']
def get_thumbnail(self, obj):
if obj.thumbnail:
request = self.context.get('request')
if request:
return absolute_https_url(obj.thumbnail.url, request)
return absolute_https_url(obj.thumbnail.url)
return None
def get_hadis_ids(self, obj):
# دریافت شناسه احادیث فعال متعلق به این کالکشن به ترتیب
if hasattr(obj, '_prefetched_objects_cache') and 'hadis_items' in obj._prefetched_objects_cache:
items = [
item.hadis_id for item in obj.hadis_items.all()
if getattr(getattr(item, 'hadis', None), 'status', True)
]
return items
return list(
obj.hadis_items.filter(hadis__status=True)
.order_by('order')
.values_list('hadis_id', flat=True)
)
class HadisSyncSerializer(serializers.ModelSerializer):
"""Serializer for syncing all hadis data (grouped fields)"""
detail = serializers.SerializerMethodField()
narrators = serializers.SerializerMethodField()
explanations = serializers.SerializerMethodField()
corrections = serializers.SerializerMethodField()
category_slug = serializers.CharField(source='category.slug', read_only=True)
interpretations = serializers.SerializerMethodField()
title = LocalizedField()
title_narrator = LocalizedField()
translation = serializers.SerializerMethodField()
class Meta:
model = Hadis
fields = [
# header (no-extend)
'id', 'slug', 'category_id', 'category_slug', 'title', 'title_narrator', 'text', 'translation',
# grouped sections
'detail', 'narrators', 'explanations', 'corrections', 'interpretations'
]
def get_translation(self, obj):
return obj.translation or []
def get_detail(self, obj):
request = self.context.get('request')
# status
status_block = None
if obj.hadis_status:
status_block = {
'id': obj.hadis_status.id,
'title': get_localized_text(obj.hadis_status.title, request),
'description': get_localized_text(obj.hadis_status.description, request),
'color': obj.hadis_status.color,
'main_color_code': obj.hadis_status.main_color_code,
}
# tags (already prefetched)
tags_block = [
{'id': tag.id, 'title': get_localized_text(tag.title, request)}
for tag in obj.tags.all()
]
# references and reference images (already prefetched)
references_block = []
reference_images_block = []
for reference in obj.references.all():
book = reference.book_reference
authors = [book.author] if book and book.author else []
references_block.append({
'id': reference.id,
'title': get_localized_text(book.title, request) if book else None,
'authors': [
{'id': a.id, 'name': get_localized_text(a.name, request)}
for a in authors
],
'description': None,
'share_link': book.share_link if book else None,
})
for img in reference.images.all():
thumb_url = None
if img.thumbnail:
thumb_url = absolute_https_url(img.thumbnail.url, request)
reference_images_block.append({
'id': img.id,
'thumbnail': thumb_url,
'priority': img.priority,
})
return {
'address': get_localized_text(obj.address, request),
'address_details': [],
'hadis_status': status_block,
'status_text': get_localized_text(obj.hadis_status_text, request),
'share_link': obj.share_link,
'links': obj.links,
'tags': tags_block,
'references': references_block,
'reference_images': reference_images_block,
}
def get_narrators(self, obj):
request = self.context.get('request')
transmitters_data = []
for transmitter_rel in obj.transmitters.all():
t = transmitter_rel.transmitter
layer = transmitter_rel.narrator_layer
reliability_block = None
if t and t.reliability:
reliability_block = {
'id': t.reliability.id,
'title': get_localized_text(t.reliability.title, request),
'slug': t.reliability.slug,
'color': t.reliability.color,
'main_color_code': t.reliability.main_color_code,
}
uncertain_list = []
if transmitter_rel.is_uncertain:
for ut in transmitter_rel.uncertain_transmitters.all():
ut_rel_block = None
if ut.reliability:
ut_rel_block = {
'id': ut.reliability.id,
'title': get_localized_text(ut.reliability.title, request),
'slug': ut.reliability.slug,
'color': ut.reliability.color,
'main_color_code': ut.reliability.main_color_code,
}
uncertain_list.append({
'id': ut.id,
'name': get_localized_text(ut.full_name, request),
'slug': ut.slug,
'known_as': get_localized_text(ut.known_as, request),
'nickname': get_localized_text(ut.nickname, request),
'birth_year_hijri': ut.birth_year_hijri,
'death_year_hijri': ut.death_year_hijri,
'reliability': ut_rel_block,
})
transmitters_data.append({
'id': t.id if t else None,
'name': get_localized_text(t.full_name, request) if t else None,
'slug': t.slug if t else None,
'known_as': get_localized_text(t.known_as, request) if t else None,
'nickname': get_localized_text(t.nickname, request) if t else None,
'reliability': reliability_block,
'layer_id': layer.id if layer else None,
'narrator_layer_id': layer.id if layer else None,
'layer_level': layer.number if layer else None,
'layer_name': get_localized_text(layer.name, request) if layer else None,
'layer_description': get_localized_text(layer.description, request) if layer else None,
'is_gap': transmitter_rel.is_gap,
'is_uncertain': transmitter_rel.is_uncertain,
'uncertain_transmitters': uncertain_list,
'birth_year_hijri': t.birth_year_hijri if t else None,
'death_year_hijri': t.death_year_hijri if t else None,
'order': transmitter_rel.order,
})
return {
'description': get_localized_text(obj.description, request),
'transmitters': transmitters_data,
}
def get_explanations(self, obj):
request = self.context.get('request')
# Return structured explanations with title and description
explanations_list = []
if hasattr(obj, 'explanations') and obj.explanations and isinstance(obj.explanations, list):
for item in obj.explanations:
if isinstance(item, dict):
lang = item.get('language_code')
# Check if matches request language
request_lang = request.query_params.get('lang', 'en') if request else 'en'
if lang == request_lang:
explanations_list.append({
'title': item.get('title', ''),
'description': item.get('description', '')
})
# If no match, return first available or fallback to old explanation field
if not explanations_list and obj.explanation:
return get_localized_text(obj.explanation, request)
return explanations_list if explanations_list else None
def get_corrections(self, obj):
"""گرفتن لیست تصحیحات بهینه‌شده مخصوص سینک"""
return HadisSyncCorrectionSerializer(
obj.hadiscorrection_set.all(),
many=True,
context=self.context
).data
def get_interpretations(self, obj):
"""گرفتن لیست تفاسیر بهینه‌شده مخصوص سینک"""
if obj.category:
return HadisSyncInterpretationSerializer(
obj.category.interpretations.all(),
many=True,
context=self.context
).data
return []
class HadisListSerializer(serializers.ModelSerializer):
"""Serializer for Hadis list"""
category = serializers.SerializerMethodField()
status = serializers.SerializerMethodField()
bookmark = serializers.SerializerMethodField()
translation = LocalizedField()
title = LocalizedField()
title_narrator = LocalizedField()
address = serializers.SerializerMethodField()
links = serializers.SerializerMethodField()
reference_images = serializers.SerializerMethodField()
images = serializers.SerializerMethodField()
class Meta:
model = Hadis
fields = ['id', 'number', 'slug', 'title', 'title_narrator', 'text',
'translation', 'category', 'status', 'bookmark', 'share_link',
'address', 'links', 'reference_images', 'images']
def get_address(self, obj):
formatted = format_address_output(obj.address)
if formatted:
return formatted
refs = list(obj.references.all())
first_ref = refs[0] if refs else None
if first_ref and hasattr(first_ref, 'address') and first_ref.address:
return format_address_output(first_ref.address)
return []
def get_links(self, obj):
return format_links_output(obj.links)
def get_reference_images(self, obj):
request = self.context.get('request')
images_list = []
for ref in obj.references.all():
for img in sorted(ref.images.all(), key=lambda x: getattr(x, 'priority', 0)):
url = None
if img.thumbnail:
url = absolute_https_url(img.thumbnail.url, request) if request else absolute_https_url(img.thumbnail.url)
elif hasattr(img, 'image') and img.image:
url = absolute_https_url(img.image.url, request) if request else absolute_https_url(img.image.url)
if url:
images_list.append({
"id": img.id,
"image": url,
"thumbnail": url,
"priority": img.priority,
})
return images_list
def get_images(self, obj):
return self.get_reference_images(obj)
def get_category(self, obj):
"""Get category id and title"""
if not obj.category:
return None
request = self.context.get('request')
# Use your helper method
title = get_localized_text(obj.category.title, request) or ""
# Safe access for sect_type to prevent crashes if sect is missing
# Note: Ensure your view uses .select_related('category__sect') for speed!
sect_type = obj.category.sect.sect_type if obj.category.sect else None
return {
'id': obj.category.id,
'title': title,
'slug': obj.category.slug,
'source_type': obj.category.source_type,
'sect_type': sect_type
}
def get_status(self, obj):
"""Get status id and title"""
if not obj.hadis_status:
return None
request = self.context.get('request')
# Use your helper method
title = get_localized_text(obj.hadis_status.title, request)
description = get_localized_text(obj.hadis_status.description, request)
return {
'id': obj.hadis_status.id,
'title': title,
'description': description,
'color': obj.hadis_status.color,
'main_color_code': obj.hadis_status.main_color_code,
}
def get_bookmark(self, obj):
"""Get bookmark information for this playlist."""
request = self.context.get('request')
user = request.user if request else None
book_mark = BookmarkStatusSerializer.get_bookmark_info(
obj=obj,
user=user,
service='hadith'
)
return book_mark.get('is_bookmarked', False)
class HadisStatusSerializer(serializers.ModelSerializer):
"""Serializer for HadisStatus"""
title = LocalizedField()
description = LocalizedField()
main_color_code = serializers.ReadOnlyField()
class Meta:
model = HadisStatus
fields = ['id', 'title', 'color', 'main_color_code', 'description']
class HadisTagSerializer(serializers.ModelSerializer):
"""Serializer for HadisTag"""
title = LocalizedField()
class Meta:
model = HadisTag
fields = ['id', 'title']
class TransmitterSerializer(serializers.ModelSerializer):
"""Serializer for Transmitters"""
full_name = LocalizedField()
arabic_full_name = ArabicLocalizedField(source='full_name')
known_as = LocalizedField()
arabic_known_as = ArabicLocalizedField(source='known_as')
nickname = LocalizedField()
arabic_nickname = ArabicLocalizedField(source='nickname')
reliability = serializers.SerializerMethodField()
share_link = serializers.CharField(read_only=True)
class Meta:
model = Transmitters
fields = [
'id', 'full_name', 'arabic_full_name', 'slug','birth_year_hijri', 'death_year_hijri',
"known_as", 'arabic_known_as', 'nickname', 'arabic_nickname', 'reliability','madhhab','generation','share_link'
]
def get_reliability(self, obj):
"""Serialize the reliability foreign key"""
if obj.reliability:
return {
'id': obj.reliability.id,
'title': get_localized_text(obj.reliability.title, self.context.get('request')),
'slug': obj.reliability.slug,
'color': obj.reliability.color,
'main_color_code': obj.reliability.main_color_code,
}
return None
class TransmitterShortSerializer(serializers.ModelSerializer):
"""Serializer for Transmitters"""
full_name = LocalizedField()
arabic_full_name = ArabicLocalizedField(source='full_name')
known_as = LocalizedField()
arabic_known_as = ArabicLocalizedField(source='known_as')
nickname = LocalizedField()
arabic_nickname = ArabicLocalizedField(source='nickname')
reliability = serializers.SerializerMethodField()
class Meta:
model = Transmitters
fields = [
'id', 'full_name', 'arabic_full_name', 'slug','birth_year_hijri', 'death_year_hijri',
"known_as", 'arabic_known_as', 'nickname', 'arabic_nickname', 'reliability'
]
def get_reliability(self, obj):
if obj.reliability:
request = self.context.get('request')
return {
'id': obj.reliability.id,
'title': get_localized_text(obj.reliability.title, request),
'slug': obj.reliability.slug,
'color': obj.reliability.color,
'main_color_code': obj.reliability.main_color_code,
}
return None
class TransmitterOpinionSerializer(serializers.ModelSerializer):
""" Serializer for TransmitterOpinions """
scholar_name = LocalizedField()
opinion_text = LocalizedField()
arabic_opinion_text = ArabicLocalizedField(source='opinion_text')
status = serializers.SerializerMethodField()
scholar = serializers.SerializerMethodField()
class Meta:
model = TransmitterOpinion
fields = ['id','transmitter','scholar_name','scholar','opinion_text','arabic_opinion_text','status']
def get_scholar(self, obj):
request = self.context.get('request')
if obj.scholar:
return {
'id': obj.scholar.id,
'name': get_localized_text(obj.scholar.name, request),
'slug': obj.scholar.slug
}
return None
def get_status(self, obj):
"""Serialize the opinion status foreign key"""
if obj.status:
request = self.context.get('request')
return {
'id': obj.status.id,
'title': get_localized_text(obj.status.title, request),
'slug': obj.status.slug,
'color': obj.status.color,
'main_color_code': obj.status.main_color_code,
}
return None
def format_address_output(ref_or_data):
if not ref_or_data:
return []
if hasattr(ref_or_data, 'address'):
addr = ref_or_data.address
else:
addr = ref_or_data
if not addr:
return []
if isinstance(addr, str):
trimmed = addr.strip()
if trimmed.startswith("[") or trimmed.startswith("{"):
try:
import json
parsed = json.loads(trimmed)
return format_address_output(parsed)
except (ValueError, TypeError):
pass
return [trimmed] if trimmed else []
elif isinstance(addr, list):
result = []
for item in addr:
if isinstance(item, str):
s = item.strip()
if s.startswith("[") or s.startswith("{"):
try:
import json
parsed = json.loads(s)
result.extend(format_address_output(parsed))
continue
except (ValueError, TypeError):
pass
if s:
result.append(s)
elif isinstance(item, dict):
# could be {'title': ['pok'], 'language_code': 'fa'} or {'title': 'pok'} or {'address': '...'} or {'text': '...'}
val = item.get('title') or item.get('address') or item.get('text') or item.get('value')
if val:
result.extend(format_address_output(val))
elif isinstance(item, list):
result.extend(format_address_output(item))
return result
return [str(addr)]
def format_links_output(links_data):
if not links_data:
return []
if isinstance(links_data, str):
trimmed = links_data.strip()
if trimmed.startswith("[") or trimmed.startswith("{"):
try:
import json
parsed = json.loads(trimmed)
return format_links_output(parsed)
except (ValueError, TypeError):
pass
return [trimmed] if trimmed else []
elif isinstance(links_data, dict):
out = []
for k, v in links_data.items():
if isinstance(v, str) and v.strip():
out.append(v.strip())
elif isinstance(v, (list, dict)):
out.extend(format_links_output(v))
return out
elif isinstance(links_data, list):
out = []
for item in links_data:
if isinstance(item, str):
s = item.strip()
if s:
out.append(s)
elif isinstance(item, dict):
url = item.get('url') or item.get('link') or item.get('href') or item.get('value')
if url and isinstance(url, str) and url.strip():
out.append(url.strip())
elif isinstance(item, list):
out.extend(format_links_output(item))
return out
return []
class TransmitterOriginalTextSerializer(serializers.ModelSerializer):
""" Serializer for TransmitterOriginalText """
title = LocalizedField()
text = LocalizedField()
translation = LocalizedField()
address = serializers.SerializerMethodField()
images = serializers.SerializerMethodField()
class Meta:
model = TransmitterOriginalText
fields = ['id', 'title', 'text', 'translation', 'share_link', 'slug', 'address', 'images']
def get_address(self, obj):
refs = list(obj.references.all())
return format_address_output(refs[0] if refs else None)
def get_images(self, obj):
request = self.context.get('request')
images_list = []
for ref in obj.references.all():
for img in ref.images.all():
if img.image:
url = absolute_https_url(img.image.url, request) if request else absolute_https_url(img.image.url)
images_list.append({
"id": img.id,
"image": url,
"thumbnail": url,
})
return images_list
class TransmitterDetailSerializer(serializers.ModelSerializer):
""" Serializer for Details of Transmitters """
full_name = LocalizedField()
arabic_full_name = ArabicLocalizedField(source='full_name')
known_as = LocalizedField()
arabic_known_as = ArabicLocalizedField(source='known_as')
nickname = LocalizedField()
arabic_nickname = ArabicLocalizedField(source='nickname')
kunya = LocalizedField()
arabic_kunya = ArabicLocalizedField(source='kunya')
origin = LocalizedField()
arabic_origin = ArabicLocalizedField(source='origin')
mawla_status = LocalizedField(source='freed_slave_of')
arabic_mawla_status = ArabicLocalizedField(source='freed_slave_of')
profession = LocalizedField(source='occupation')
arabic_profession = ArabicLocalizedField(source='occupation')
physical_traits = LocalizedField(source='features')
arabic_physical_traits = ArabicLocalizedField(source='features')
lived_in = LocalizedField()
died_in = LocalizedField()
description = LocalizedField()
companion_type = LocalizedField()
arabic_companion_type = ArabicLocalizedField(source='companion_type')
reliability = serializers.SerializerMethodField()
relatives = serializers.SerializerMethodField()
father = serializers.SerializerMethodField()
sons = serializers.SerializerMethodField()
share_link = serializers.CharField(read_only=True)
class Meta:
model = Transmitters
fields = [
'id','full_name', 'arabic_full_name', 'kunya', 'arabic_kunya', 'known_as', 'arabic_known_as', 'nickname', 'arabic_nickname',
'origin', 'arabic_origin', 'mawla_status', 'arabic_mawla_status', 'profession', 'arabic_profession', 'physical_traits', 'arabic_physical_traits',
'lived_in','died_in','birth_year_hijri',
'death_year_hijri','age_at_death','reliability',
'madhhab',"in_sahih_muslim","in_sahih_bukhari",
"description",'generation','share_link',
'tadlis', 'ikhtilat', 'companion_type', 'arabic_companion_type', 'relatives',
'father', 'sons'
]
def get_father(self, obj):
"""Serialize the father foreign key or relative"""
father_obj = obj.father
request = self.context.get('request')
if not father_obj:
parent_rel = obj.relatives.filter(relation_type__in=['parents', 'stepfather']).first()
if parent_rel:
if parent_rel.narrator:
father_obj = parent_rel.narrator
elif parent_rel.name:
return {
'id': None,
'name': parent_rel.name,
'arabic_name': parent_rel.name,
'slug': None,
}
if father_obj:
return {
'id': father_obj.id,
'name': get_localized_text(father_obj.full_name, request) or (father_obj.get_name('en') if hasattr(father_obj, 'get_name') else None) or '',
'arabic_name': get_arabic_localized_text(father_obj.full_name) or (father_obj.get_name('ar') if hasattr(father_obj, 'get_name') else None),
'slug': father_obj.slug,
}
return None
def get_sons(self, obj):
"""Serialize all sons (from children_as_father FK and TransmitterRelative)"""
request = self.context.get('request')
result = []
seen_ids = set()
for child in obj.children_as_father.all():
seen_ids.add(child.id)
result.append({
'id': child.id,
'name': get_localized_text(child.full_name, request) or (child.get_name('en') if hasattr(child, 'get_name') else None) or '',
'arabic_name': get_arabic_localized_text(child.full_name) or (child.get_name('ar') if hasattr(child, 'get_name') else None),
'slug': child.slug,
})
for rel in obj.relatives.filter(relation_type='children'):
if rel.narrator and rel.narrator.id not in seen_ids:
seen_ids.add(rel.narrator.id)
result.append({
'id': rel.narrator.id,
'name': get_localized_text(rel.narrator.full_name, request) or (rel.narrator.get_name('en') if hasattr(rel.narrator, 'get_name') else None) or '',
'arabic_name': get_arabic_localized_text(rel.narrator.full_name) or (rel.narrator.get_name('ar') if hasattr(rel.narrator, 'get_name') else None),
'slug': rel.narrator.slug,
})
elif not rel.narrator and rel.name:
result.append({
'id': None,
'name': rel.name,
'arabic_name': rel.name,
'slug': None,
})
return result
def get_reliability(self, obj):
"""Serialize the reliability foreign key"""
if obj.reliability:
return {
'id': obj.reliability.id,
'title': get_localized_text(obj.reliability.title, self.context.get('request')),
'slug': obj.reliability.slug,
'color': obj.reliability.color,
'main_color_code': obj.reliability.main_color_code,
}
return None
def get_relatives(self, obj):
request = self.context.get('request')
relatives_qs = obj.relatives.all()
grouped = {}
for rel in relatives_qs:
rel_type = rel.relation_type
slug = rel.narrator.slug if rel.narrator else None
# Use narrator's localized name if exists and name is empty, otherwise use the direct name
name = rel.name
if rel.narrator and not name:
name = get_localized_text(rel.narrator.full_name, request)
item = {
"name": name,
"slug": slug
}
if rel_type not in grouped:
grouped[rel_type] = {
"relation_value": get_localized_text(rel.relation_value, request) if rel.relation_value else None,
"arabic_relation_value": get_arabic_localized_text(rel.relation_value) if rel.relation_value else None,
"items": []
}
grouped[rel_type]["items"].append(item)
result = []
for rel_type, data in grouped.items():
result.append({
"relation_type": rel_type,
"relation_value": data["relation_value"],
"arabic_relation_value": data["arabic_relation_value"],
"members": data["items"]
})
return result
class TransmittersFiltersSerializer(serializers.ModelSerializer):
pass
class TransmitterSyncSerializer(serializers.ModelSerializer):
"""Serializer for syncing all transmitter data for offline mode"""
biographical = serializers.SerializerMethodField()
scholars_opinions = serializers.SerializerMethodField()
original_texts = serializers.SerializerMethodField()
full_name = LocalizedField()
arabic_full_name = ArabicLocalizedField(source='full_name')
hadis = serializers.SerializerMethodField()
relations = serializers.SerializerMethodField()
class Meta:
model = Transmitters
fields = [
'id', 'full_name', 'arabic_full_name', 'slug', 'biographical', 'scholars_opinions', 'original_texts','hadis','relations'
]
def get_biographical(self, obj):
"""Get biographical information (flattened)"""
request = self.context.get('request') # ← FIX: Define request
if obj.reliability:
r= {
'id': obj.reliability.id,
'title': get_localized_text(obj.reliability.title, self.context.get('request')),
'color': obj.reliability.color,
'main_color_code': obj.reliability.main_color_code,
}
else :
r= None
return {
'full_name': get_localized_text(obj.full_name, request),
'arabic_full_name': get_arabic_localized_text(obj.full_name),
'kunya': get_localized_text(obj.kunya, request),
'arabic_kunya': get_arabic_localized_text(obj.kunya),
'known_as': get_localized_text(obj.known_as, request),
'arabic_known_as': get_arabic_localized_text(obj.known_as),
'nickname': get_localized_text(obj.nickname, request),
'arabic_nickname': get_arabic_localized_text(obj.nickname),
'origin': get_localized_text(obj.origin, request),
'arabic_origin': get_arabic_localized_text(obj.origin),
'mawla_status': get_localized_text(obj.freed_slave_of, request),
'arabic_mawla_status': get_arabic_localized_text(obj.freed_slave_of),
'profession': get_localized_text(obj.occupation, request),
'arabic_profession': get_arabic_localized_text(obj.occupation),
'physical_traits': get_localized_text(obj.features, request),
'arabic_physical_traits': get_arabic_localized_text(obj.features),
'lived_in': get_localized_text(obj.lived_in, request),
'died_in': get_localized_text(obj.died_in, request),
'birth_year_hijri': obj.birth_year_hijri,
'death_year_hijri': obj.death_year_hijri,
'age_at_death': obj.age_at_death,
'generation': obj.generation,
'reliability': r,
'madhhab': obj.madhhab,
'in_sahih_muslim': obj.in_sahih_muslim,
'in_sahih_bukhari': obj.in_sahih_bukhari,
'description': get_localized_text(obj.description, request),
'thumbnail': obj.thumbnail.url if obj.thumbnail else None,
'share_link': obj.share_link,
'tadlis': obj.tadlis,
'ikhtilat': obj.ikhtilat,
'companion_type': get_localized_text(obj.companion_type, request),
'arabic_companion_type': get_arabic_localized_text(obj.companion_type),
'relatives': self._get_relatives(obj, request),
}
def _get_relatives(self, obj, request):
relatives_qs = obj.relatives.all()
grouped = {}
for rel in relatives_qs:
rel_type = rel.relation_type
slug = rel.narrator.slug if rel.narrator else None
name = rel.name
if rel.narrator and not name:
name = get_localized_text(rel.narrator.full_name, request)
item = {
"name": name,
"slug": slug
}
if rel_type not in grouped:
grouped[rel_type] = {
"relation_value": get_localized_text(rel.relation_value, request) if rel.relation_value else None,
"arabic_relation_value": get_arabic_localized_text(rel.relation_value) if rel.relation_value else None,
"items": []
}
grouped[rel_type]["items"].append(item)
result = []
for rel_type, data in grouped.items():
result.append({
"relation_type": rel_type,
"relation_value": data["relation_value"],
"arabic_relation_value": data["arabic_relation_value"],
"members": data["items"]
})
return result
def get_scholars_opinions(self, obj):
"""Get all scholarly opinions about this transmitter"""
request = self.context.get('request') # ← FIX: Define request
return [
{
'id': opinion.id,
'scholar_name': get_localized_text(opinion.scholar_name, request),
'arabic_scholar_name': get_arabic_localized_text(opinion.scholar_name),
'scholar': {
'id': opinion.scholar.id,
'name': get_localized_text(opinion.scholar.name, request),
'slug': opinion.scholar.slug
} if opinion.scholar else None,
'opinion_text': get_localized_text(opinion.opinion_text, request),
'arabic_opinion_text': get_arabic_localized_text(opinion.opinion_text),
'status': {
'id': opinion.status.id,
'title': get_localized_text(opinion.status.title, request),
'slug': opinion.status.slug,
'color': opinion.status.color,
'main_color_code': opinion.status.main_color_code,
} if opinion.status else None,
'created_at': opinion.created_at.isoformat() if opinion.created_at else None,
'updated_at': opinion.updated_at.isoformat() if opinion.updated_at else None,
}
for opinion in obj.opinions.all() # Already prefetched
]
def get_original_texts(self, obj):
"""گرفتن متون اصلی راوی با جزئیات کامل و رفرنس‌ها"""
# استفاده از سریالایزر Detail که در مرحله قبل ساختیم
return TransmitterOriginalTextDetailSerializer(
obj.originaltexts.all(),
many=True,
context=self.context
).data
def get_hadis(self, obj):
categories_map = self.context.get('categories_map', {})
return list(categories_map.get(int(obj.id), set()))
def get_relations(self, obj):
"""
استخراج آنی آیدی اساتید و شاگردان از کانتکست بدون اجرای کوئری اضافه
"""
teachers_map = self.context.get('teachers_map', {})
students_map = self.context.get('students_map', {})
# حتماً آیدی را به عدد صحیح تبدیل می‌کنیم تا از مچ شدن با دیکشنری مطمئن شویم
obj_id = int(obj.id)
return {
"teachers": list(teachers_map.get(obj_id, set())),
"students": list(students_map.get(obj_id, set()))
}
class HadisTransmitterSerializer(serializers.ModelSerializer):
"""Serializer for HadisTransmitter with transmitter details"""
transmitter = TransmitterShortSerializer(read_only=True)
uncertain_transmitters = TransmitterShortSerializer(many=True, read_only=True)
layer = serializers.SerializerMethodField()
status = serializers.SerializerMethodField()
class Meta:
model = HadisTransmitter
fields = [
'id', 'order', 'is_gap', 'is_uncertain', 'layer', 'transmitter', 'uncertain_transmitters', 'status'
]
def get_layer(self, obj):
"""Get narrator layer slug"""
return obj.narrator_layer.slug if obj.narrator_layer else None
def get_status(self, obj):
"""Serialize the status foreign key"""
if obj.status:
request = self.context.get('request')
return {
'id': obj.status.id,
'title': get_localized_text(obj.status.title, request),
'slug': obj.status.slug,
'color': obj.status.color,
'main_color_code': obj.status.main_color_code,
}
return None
# serializers.py
class HadisTransmitterListSerializer(serializers.ModelSerializer):
"""
The 'Parent' Serializer.
It takes a HADIS object and returns the count + the list of transmitters.
"""
layer_count = serializers.SerializerMethodField()
layer_names = serializers.SerializerMethodField()
results = HadisTransmitterSerializer(
source='transmitters', # Access the 'transmitters' reverse relation
many=True,
read_only=True
)
class Meta:
model = Hadis
fields = ['id', 'layer_count','layer_names', 'results']
def get_layer_count(self, obj):
return obj.narrator_layers.count()
def get_layer_names(self, obj):
"""Get list of localized narrator layer names"""
request = self.context.get('request')
layer_objects = obj.narrator_layers.all().order_by('number')
layer_names = []
for layer in layer_objects:
name = get_localized_text(layer.name, request=request)
description = get_localized_text(layer.description, request=request)
slug = layer.slug
layer_names.append({
'id': layer.id,
'name': name,
'slug': slug,
'description': description
})
return layer_names
class ReferenceImageSerializer(serializers.ModelSerializer):
"""Serializer for ReferenceImage"""
thumbnail = serializers.SerializerMethodField()
class Meta:
model = ReferenceImage
fields = ['id', 'thumbnail', 'priority']
def get_thumbnail(self, obj):
"""Get thumbnail URL"""
if obj.thumbnail:
request = self.context.get('request')
if request:
return absolute_https_url(obj.thumbnail.url, request)
return absolute_https_url(obj.thumbnail.url)
return None
class HadisReferenceSerializer(serializers.ModelSerializer):
"""Serializer for HadisReference with book and images"""
book_title = serializers.SerializerMethodField()
book_authors = serializers.SerializerMethodField()
book_description = serializers.SerializerMethodField()
share_link = serializers.CharField(source='book_reference.share_link', read_only=True)
class Meta:
model = HadisReference
fields = [
'id', 'book_title', 'book_description','book_authors','share_link'
]
# def get_type_name(self, obj):
# """Get localized type name from book_reference.type"""
# if obj.book_reference and obj.book_reference.type:
# field = LocalizedField()
# field._context = self.context
# return field.to_representation(obj.book_reference.type.title)
# return None
# def get_subject_area(self, obj):
# """Get list of localized subject area names"""
# if obj.book_reference and obj.book_reference.subject_area.exists():
# subject_areas = []
# field = LocalizedField()
# field._context = self.context
# for subject in obj.book_reference.subject_area.all():
# subject_areas.append(field.to_representation(subject.title))
# return subject_areas
# return []
# def get_book_english_name(self,obj):
# english_name_data = obj.book_reference.title
# for item in english_name_data:
# if item.get('language_code') == 'en':
# return item.get('text')
# return None
# def get_book_language(self,obj):
# language_data = obj.book_reference.language
# field = LocalizedField()
# field._context = self.context
# return field.to_representation(language_data)
def get_book_title(self, obj):
title_data = obj.book_reference.title
field = LocalizedField()
field._context = self.context
return field.to_representation(title_data)
def get_book_authors(self, obj):
"""Get list of localized author names"""
try:
# 1. Fetch the BookAuthor objects
authors = [obj.book_reference.author] if obj.book_reference and obj.book_reference.author else []
if not authors:
return []
# 2. Prepare your localized field helper
field = LocalizedField()
field._context = self.context
# 3. Loop through authors and convert each object to a string (name)
# Result will be like: ["Al-Bukhari", "Muslim"]
out = []
for author in authors:
name = field.to_representation(author.name)
# Format birth and death dates (Hijri/CE)
birth_hijri = author.birth_year_hijri
birth_miladi = author.birth_year_miladi
death_hijri = author.death_year_hijri
death_miladi = author.death_year_miladi
birth_str = ""
if birth_hijri and birth_miladi:
birth_str = f"{birth_hijri}AH/{birth_miladi}CE"
elif birth_hijri:
birth_str = f"{birth_hijri}AH"
elif birth_miladi:
birth_str = f"{birth_miladi}CE"
death_str = ""
if death_hijri and death_miladi:
death_str = f"{death_hijri}AH/{death_miladi}CE"
elif death_hijri:
death_str = f"{death_hijri}AH"
elif death_miladi:
death_str = f"{death_miladi}CE"
out.append({
'name': name,
'birth': birth_str,
'death': death_str,
})
return out
except Exception:
return []
def get_book_description(self, obj):
description_data = obj.book_reference.description
field = LocalizedField()
field._context = self.context
return field.to_representation(description_data)
class HadisCorrectionSerializer(serializers.ModelSerializer):
"""Serializer for HadisCorrection"""
title = LocalizedField()
description = serializers.SerializerMethodField()
translation = LocalizedField()
bookmark = serializers.SerializerMethodField()
address = serializers.SerializerMethodField()
images = serializers.SerializerMethodField()
links = serializers.JSONField(read_only=True)
hadis_info = serializers.SerializerMethodField()
class Meta:
model = HadisCorrection
fields = ['id', 'title', 'slug', 'narrator', 'description', 'translation', 'share_link', 'bookmark', 'address', 'images', 'links', 'hadis_info']
def get_hadis_info(self, obj):
if obj.hadis:
request = self.context.get('request')
return {
"id": obj.hadis.id,
"number": obj.hadis.number,
"slug": obj.hadis.slug,
"title": get_localized_text(obj.hadis.title, request) if hasattr(obj.hadis, 'title') and obj.hadis.title else f"Hadith {obj.hadis.number}",
}
return None
def get_description(self, obj):
request = self.context.get('request')
return get_localized_correction_text(obj.text, request=request)
def get_bookmark(self, obj):
"""Get bookmark information for this correction."""
request = self.context.get('request')
user = request.user if request else None
book_mark = BookmarkStatusSerializer.get_bookmark_info(
obj=obj,
user=user,
service='hadith_correction'
)
return book_mark.get('is_bookmarked', False)
def get_address(self, obj):
refs = list(obj.references.all())
return format_address_output(refs[0] if refs else None)
def get_images(self, obj):
request = self.context.get('request')
images_list = []
for ref in obj.references.all():
for img in ref.images.all():
if img.image:
url = absolute_https_url(img.image.url, request) if request else absolute_https_url(img.image.url)
images_list.append({
"id": img.id,
"image": url,
"thumbnail": url,
})
return images_list
class HadisBasicSerializer(serializers.ModelSerializer):
"""Basic serializer for Hadis with minimal information"""
translation = LocalizedField()
category = serializers.SerializerMethodField()
bookmark= serializers.SerializerMethodField()
title = LocalizedField()
title_narrator = LocalizedField()
explanation = LocalizedField()
# explanations = serializers.SerializerMethodField()
class Meta:
model = Hadis
fields = [
'id', 'slug', 'title','bookmark', 'title_narrator', 'text',
'translation', 'share_link','explanation','category'
]
# def get_explanations(self, obj):
# """Get structured explanations for the requested language"""
# request = self.context.get('request')
# request_lang = request.query_params.get('lang', 'en') if request else 'en'
# explanations_list = []
# if obj.explanations and isinstance(obj.explanations, list):
# for item in obj.explanations:
# if isinstance(item, dict) and item.get('language_code') == request_lang:
# explanations_list.append({
# 'title': item.get('title', ''),
# 'description': item.get('description', '')
# })
# return explanations_list if explanations_list else None
def get_category(self, obj):
"""Get category id and title"""
if obj.category:
request = self.context.get('request')
return {
'id': obj.category.id,
'title': get_localized_text(obj.title,request),
'slug':obj.category.slug,
'source_type':obj.category.source_type,
'sect_type':obj.category.sect.sect_type
}
return None
def get_bookmark(self, obj):
"""Get bookmark information for this playlist."""
request = self.context.get('request')
user = request.user if request else None
book_mark = BookmarkStatusSerializer.get_bookmark_info(
obj=obj,
user=user,
service='hadith'
)
return book_mark.get('is_bookmarked', False)
class HadisShortSerializer(serializers.ModelSerializer):
"""Basic serializer for Hadis with minimal information"""
translation = LocalizedField()
title = LocalizedField()
title_narrator = LocalizedField()
bookmark = serializers.SerializerMethodField()
class Meta:
model = Hadis
fields = [
'id', 'slug', 'title', 'title_narrator', 'text',
'translation', 'share_link', 'bookmark']
def get_bookmark(self, obj):
"""Get bookmark status for this Hadis."""
request = self.context.get('request')
user = request.user if request else None
book_mark = BookmarkStatusSerializer.get_bookmark_info(
obj=obj,
user=user,
service='hadith'
)
return book_mark.get('is_bookmarked', False)
class HadisDetailSerializer(serializers.ModelSerializer):
"""Detailed serializer for Hadis with core information (transmitters and corrections moved to separate endpoints)"""
hadis_status = HadisStatusSerializer(read_only=True)
status_text = LocalizedField(source='hadis_status_text')
tags = HadisTagSerializer(many=True, read_only=True)
references = HadisReferenceSerializer(
many=True,
read_only=True
)
reference_images = SerializerMethodField()
address = LocalizedField()
category_slug = serializers.CharField(source='category.slug', read_only=True)
class Meta:
model = Hadis
fields = [
'id', 'number', 'slug', 'category_id', 'category_slug', 'hadis_status', 'status_text', 'links', 'share_link',
'tags', 'references', 'reference_images', 'address'
]
# def get_address_details(self, obj):
# """Get address details sorted by priority"""
# if obj.address_details and isinstance(obj.address_details, list):
# return sorted(obj.address_details, key=lambda x: x.get('priority', 0))
# return []
def get_reference_images(self, obj):
"""Get all reference images from all references"""
all_images = []
for reference in obj.references.all():
images = reference.images.all().order_by('priority')
serializer = ReferenceImageSerializer(images, many=True, context=self.context)
all_images.extend(serializer.data)
return all_images
# def get_category(self, obj):
# """Get category details"""
# if obj.category:
# return {
# 'id': obj.category.id,
# 'title': obj.category.title,
# 'category_type': obj.category.content_type
# }
# return None
def get_translation(self, obj):
"""Get translation based on request language"""
request = self.context.get('request')
language_code = getattr(request, 'LANGUAGE_CODE', 'en')
return obj.translation.get(language_code)
class NarratorLayerSerializer(serializers.Serializer):
"""Serializer for narrator layers information"""
name = serializers.SerializerMethodField()
slug = serializers.CharField(read_only=True)
description = serializers.SerializerMethodField()
def get_name(self, obj):
"""Get localized name"""
request = self.context.get('request')
return get_localized_text(obj.name, request=request)
def get_description(self, obj):
"""Get localized description"""
request = self.context.get('request')
return get_localized_text(obj.description, request=request)
class NarratorRelationSerializer(serializers.ModelSerializer):
"""
سریالایزر سبک برای نمایش راویان به عنوان استاد یا شاگرد
"""
full_name = LocalizedField()
known_as = LocalizedField()
reliability = SerializerMethodField()
class Meta:
model = Transmitters
fields = ['id', 'slug', 'full_name', 'known_as', 'birth_year_hijri', 'death_year_hijri','reliability']
def get_reliability(self, obj):
"""Serialize the reliability foreign key"""
if obj.reliability:
return {
'id': obj.reliability.id,
'title': get_localized_text(obj.reliability.title, self.context.get('request')),
'color': obj.reliability.color,
'main_color_code': obj.reliability.main_color_code,
}
# --- سریالایزرهای کمکی تصاویر رفرنس ---
class CorrectionReferenceImageSerializer(serializers.ModelSerializer):
image = serializers.SerializerMethodField()
thumbnail = serializers.SerializerMethodField()
class Meta:
model = CorrectionReferenceImage
fields = ['id', 'image', 'thumbnail', 'priority']
def get_image(self, obj):
if obj.image:
request = self.context.get('request')
return absolute_https_url(obj.image.url, request) if request else absolute_https_url(obj.image.url)
return None
def get_thumbnail(self, obj):
return self.get_image(obj)
class InterpretationReferenceImageSerializer(serializers.ModelSerializer):
class Meta:
model = InterpretationReferenceImage
fields = ['id', 'image', 'priority']
class OriginalTextReferenceImageSerializer(serializers.ModelSerializer):
class Meta:
model = OriginalTextReferenceImage
fields = ['id', 'image', 'priority']
# --- سریالایزرهای جامع رفرنس‌ها (شامل اطلاعات کتاب، ادیشن و ولوم) ---
class BaseDetailedReferenceSerializer(serializers.ModelSerializer):
book_title = serializers.SerializerMethodField()
book_authors = serializers.SerializerMethodField()
edition_info = serializers.SerializerMethodField()
volume_info = serializers.SerializerMethodField()
def get_book_title(self, obj):
if obj.book_reference:
return get_localized_text(obj.book_reference.title, self.context.get('request'))
return None
def get_book_authors(self, obj):
if obj.book_reference:
request = self.context.get('request')
authors = [obj.book_reference.author] if obj.book_reference.author else []
return [
{"id": author.id, "name": get_localized_text(author.name, request)}
for author in authors
]
return []
def get_edition_info(self, obj):
edition = obj.edition
if not edition and getattr(obj, "book_volume", None) and obj.book_volume.edition:
edition = obj.book_volume.edition
if not edition and getattr(obj, "book_reference", None) and hasattr(obj.book_reference, "editions"):
edition = obj.book_reference.editions.first()
if edition:
request = self.context.get("request")
pub = edition.publisher
if isinstance(pub, list):
pub = get_localized_text(pub, request)
return {
"id": edition.id,
"edition_number": edition.edition_number,
"publisher": pub or "",
"year_of_publication": edition.year_of_publication or "",
"number_of_volumes": edition.number_of_volumes,
}
return None
def get_volume_info(self, obj):
if obj.book_volume:
return {
"id": obj.book_volume.id,
"title": obj.book_volume.title
}
return None
class DetailedCorrectionReferenceSerializer(BaseDetailedReferenceSerializer):
book = serializers.SerializerMethodField()
author = serializers.SerializerMethodField()
edition = serializers.SerializerMethodField()
open_book_url = serializers.SerializerMethodField()
citation_summary = serializers.SerializerMethodField()
images = CorrectionReferenceImageSerializer(many=True, read_only=True)
images_count = serializers.SerializerMethodField()
class Meta:
model = CorrectionReference
fields = [
'id',
'book',
'author',
'edition',
'volume_info',
'hadith_number',
'volume',
'pages',
'address',
'url',
'open_book_url',
'citation_summary',
'images',
'images_count',
'book_title',
'book_authors',
'edition_info',
]
def get_book(self, obj):
if obj.book_reference:
request = self.context.get('request')
first_ed = obj.edition
if not first_ed and getattr(obj, "book_volume", None) and obj.book_volume.edition:
first_ed = obj.book_volume.edition
if not first_ed and hasattr(obj.book_reference, "editions"):
first_ed = obj.book_reference.editions.first()
pub = getattr(obj.book_reference, "publisher", None)
if pub is None and first_ed:
pub = first_ed.publisher
if isinstance(pub, list):
pub = get_localized_text(pub, request)
year = getattr(obj.book_reference, "year_of_publication", None)
if not year and first_ed:
year = first_ed.year_of_publication
num_vols = getattr(obj.book_reference, "number_of_volumes", None)
if num_vols is None and first_ed:
num_vols = first_ed.number_of_volumes
if num_vols is None and hasattr(obj.book_reference, "volumes"):
num_vols = obj.book_reference.volumes.count() or None
return {
'id': obj.book_reference.id,
'slug': obj.book_reference.slug,
'title': get_localized_text(obj.book_reference.title, request),
'publisher': pub or '',
'year_of_publication': year or '',
'number_of_volumes': num_vols,
}
return None
def get_author(self, obj):
if obj.book_reference and obj.book_reference.author:
author = obj.book_reference.author
request = self.context.get('request')
return {
'id': author.id,
'slug': author.slug,
'name': get_localized_text(author.name, request)
}
return None
def get_edition(self, obj):
edition = obj.edition
if not edition and getattr(obj, "book_volume", None) and obj.book_volume.edition:
edition = obj.book_volume.edition
if not edition and getattr(obj, "book_reference", None) and hasattr(obj.book_reference, "editions"):
edition = obj.book_reference.editions.first()
if edition:
request = self.context.get('request')
pub = edition.publisher
if isinstance(pub, list):
pub = get_localized_text(pub, request)
return {
'id': edition.id,
'edition_number': edition.edition_number,
'publisher': pub or '',
'year_of_publication': edition.year_of_publication or '',
'number_of_volumes': edition.number_of_volumes,
}
return None
def get_open_book_url(self, obj):
request = self.context.get('request')
if obj.book_volume and obj.book_volume.file:
return absolute_https_url(obj.book_volume.file.url, request) if request else absolute_https_url(obj.book_volume.file.url)
return None
volume = serializers.SerializerMethodField()
pages = serializers.SerializerMethodField()
hadith_number = serializers.SerializerMethodField()
def get_volume(self, obj):
if obj.volume and str(obj.volume).strip().lower() != "none":
return obj.volume
if obj.book_volume and obj.book_volume.title:
return obj.book_volume.title
return None
def get_pages(self, obj):
if obj.pages and str(obj.pages).strip().lower() != "none":
return obj.pages
return None
def get_hadith_number(self, obj):
if obj.hadith_number and str(obj.hadith_number).strip().lower() != "none":
return obj.hadith_number
return None
def get_citation_summary(self, obj):
parts = []
vol = self.get_volume(obj)
pg = self.get_pages(obj)
num = self.get_hadith_number(obj)
if vol:
vol_str = str(vol).strip()
if vol_str.startswith("جلد"):
parts.append(vol_str)
else:
parts.append(f"جلد {vol_str}")
if pg:
pg_str = str(pg).strip()
if pg_str.startswith("ص") or pg_str.startswith("صفحه"):
parts.append(pg_str)
else:
parts.append(f"صفحه {pg_str}")
if num:
num_str = str(num).strip()
if num_str.startswith("ح") or num_str.startswith("حدیث") or num_str.startswith("شماره"):
parts.append(num_str)
else:
parts.append(f"شماره حدیث {num_str}")
return "".join(parts) if parts else ""
def get_images_count(self, obj):
if hasattr(obj, '_prefetched_objects_cache') and 'images' in obj._prefetched_objects_cache:
return len(obj.images.all())
return obj.images.count()
class DetailedInterpretationReferenceSerializer(BaseDetailedReferenceSerializer):
images = InterpretationReferenceImageSerializer(many=True, read_only=True)
class Meta:
model = InterpretationReference
fields = ['id', 'book_title', 'book_authors', 'edition_info', 'volume_info', 'hadith_number', 'volume', 'pages', 'address', 'url', 'images']
class DetailedOriginalTextReferenceSerializer(BaseDetailedReferenceSerializer):
images = OriginalTextReferenceImageSerializer(many=True, read_only=True)
class Meta:
model = OriginalTextReference
fields = ['id', 'book_title', 'book_authors', 'edition_info', 'volume_info', 'volume', 'pages', 'address', 'url', 'images']
# --- سریالایزرهای اصلی جزئیات (Detail Serializers) ---
class HadisCorrectionDetailSerializer(serializers.ModelSerializer):
title = LocalizedField()
description = serializers.SerializerMethodField()
translation = LocalizedField()
references = DetailedCorrectionReferenceSerializer(many=True, read_only=True)
hadis_info = serializers.SerializerMethodField()
address = serializers.SerializerMethodField()
images = serializers.SerializerMethodField()
class Meta:
model = HadisCorrection
fields = ['id', 'title', 'slug', 'narrator', 'description', 'translation', 'share_link', 'hadis_info', 'address', 'images', 'references', 'links']
def get_description(self, obj):
return get_localized_correction_text(obj.text, request=self.context.get('request'))
def get_hadis_info(self, obj):
return {"id": obj.hadis.id, "number": obj.hadis.number, "slug": obj.hadis.slug}
def get_address(self, obj):
refs = list(obj.references.all())
return format_address_output(refs[0] if refs else None)
def get_images(self, obj):
request = self.context.get('request')
images_list = []
for ref in obj.references.all():
for img in ref.images.all():
if img.image:
url = absolute_https_url(img.image.url, request) if request else absolute_https_url(img.image.url)
images_list.append({
"id": img.id,
"image": url,
"thumbnail": url,
})
return images_list
class HadisInterpretationDetailSerializer(serializers.ModelSerializer):
title = LocalizedField()
translation = LocalizedField()
references = DetailedInterpretationReferenceSerializer(many=True, read_only=True)
category_info = serializers.SerializerMethodField()
address = serializers.SerializerMethodField()
images = serializers.SerializerMethodField()
class Meta:
model = HadisInterpretation
fields = ['id', 'title', 'slug', 'narrator', 'text', 'translation', 'category_info', 'address', 'images', 'links', 'references']
def get_category_info(self, obj):
return {
"id": obj.category.id,
"slug": obj.category.slug,
"title": get_localized_text(obj.category.title, self.context.get('request'))
}
def get_address(self, obj):
refs = list(obj.references.all())
return format_address_output(refs[0] if refs else None)
def get_images(self, obj):
request = self.context.get('request')
images_list = []
for ref in obj.references.all():
for img in ref.images.all():
if img.image:
url = absolute_https_url(img.image.url, request) if request else absolute_https_url(img.image.url)
images_list.append({
"id": img.id,
"image": url,
"thumbnail": url,
})
return images_list
class HadisSyncCorrectionSerializer(HadisCorrectionDetailSerializer):
"""نسخه سبک و بهینه تصحیحات مخصوص سینک بدون hadis_info"""
class Meta(HadisCorrectionDetailSerializer.Meta):
fields = ['id', 'title', 'slug', 'narrator', 'description', 'translation', 'share_link', 'address', 'images', 'references', 'links']
class HadisSyncInterpretationSerializer(HadisInterpretationDetailSerializer):
"""نسخه سبک و بهینه تفاسیر مخصوص سینک بدون category_info"""
class Meta(HadisInterpretationDetailSerializer.Meta):
fields = ['id', 'title', 'slug', 'narrator', 'text', 'translation', 'address', 'images', 'links', 'references']
class TransmitterOriginalTextDetailSerializer(serializers.ModelSerializer):
title = LocalizedField()
text = LocalizedField()
translation = LocalizedField()
references = DetailedOriginalTextReferenceSerializer(many=True, read_only=True)
transmitter_info = serializers.SerializerMethodField()
address = serializers.SerializerMethodField()
images = serializers.SerializerMethodField()
class Meta:
model = TransmitterOriginalText
fields = ['id', 'slug', 'title', 'text', 'translation', 'share_link', 'transmitter_info', 'address', 'images', 'references']
def get_transmitter_info(self, obj):
return {
"id": obj.transmitter.id,
"slug": obj.transmitter.slug,
"name": get_localized_text(obj.transmitter.full_name, self.context.get('request'))
}
def get_address(self, obj):
refs = list(obj.references.all())
return format_address_output(refs[0] if refs else None)
def get_images(self, obj):
request = self.context.get('request')
images_list = []
for ref in obj.references.all():
for img in ref.images.all():
if img.image:
url = absolute_https_url(img.image.url, request) if request else absolute_https_url(img.image.url)
images_list.append({
"id": img.id,
"image": url,
"thumbnail": url,
})
return images_list
class HadisSourceDetailsSerializer(serializers.ModelSerializer):
"""
Lightweight serializer for Hadis Source Details (Address, Links, Images).
Used on-demand when opening source detail modals in web and mobile clients.
"""
title = LocalizedField()
narrator = LocalizedField(source='title_narrator')
source_type = serializers.SerializerMethodField()
address = serializers.SerializerMethodField()
links = serializers.SerializerMethodField()
images = serializers.SerializerMethodField()
class Meta:
model = Hadis
fields = [
'id', 'slug', 'title', 'narrator', 'source_type',
'address', 'links', 'images', 'share_link'
]
def get_source_type(self, obj):
if obj.category and obj.category.source_type:
return obj.category.source_type
return 'hadith'
def get_address(self, obj):
# 1. Primary: obj.address
formatted = format_address_output(obj.address)
if formatted:
return formatted
# 2. Fallback: references address
refs = list(obj.references.all())
first_ref = refs[0] if refs else None
if first_ref and hasattr(first_ref, 'address') and first_ref.address:
return format_address_output(first_ref.address)
return []
def get_links(self, obj):
return format_links_output(obj.links)
def get_images(self, obj):
request = self.context.get('request')
images_list = []
for ref in obj.references.all():
for img in sorted(ref.images.all(), key=lambda x: getattr(x, 'priority', 0)):
url = None
if img.thumbnail:
url = absolute_https_url(img.thumbnail.url, request) if request else absolute_https_url(img.thumbnail.url)
elif hasattr(img, 'image') and img.image:
url = absolute_https_url(img.image.url, request) if request else absolute_https_url(img.image.url)
if url:
images_list.append({
"id": img.id,
"image": url,
"thumbnail": url,
"priority": img.priority,
})
return images_list