from rest_framework import serializers from django.core.files.uploadedfile import SimpleUploadedFile from utils.image_compression import maybe_compress_uploaded_file from ..models import ( Hadis, HadisCategory, HadisStatus, HadisTag, HadisCorrection, HadisInterpretation, HadisCollection, HadisSect, HadisInCollection, HadisTransmitter, BookReference, BookReferenceImage, BookReferenceDocument, BookAuthor, BookAttribute, BookSubjectArea, BookType, BookEdition, BookVolume, Transmitters, TransmitterReliability, NarratorLayer, OpinionStatus, TransmitterOpinion, TransmitterOriginalText, CorrectionReference, CorrectionReferenceImage, InterpretationReference, InterpretationReferenceImage, OriginalTextReference, OriginalTextReferenceImage ) from .category import LocalizedField class AbsoluteImageField(serializers.ImageField): def to_representation(self, value): if not value: return None request = self.context.get("request") url = value.url if hasattr(value, "url") else str(value) if request: return request.build_absolute_uri(url) return url class AdminHadisCategorySerializer(serializers.ModelSerializer): sect_type = serializers.CharField(source='sect.sect_type', read_only=True) title = LocalizedField() description = LocalizedField() class Meta: model = HadisCategory fields = [ "id", "title", "slug", "source_type", "order", "sect", "sect_type", "description", ] class AdminHadisStatusSerializer(serializers.ModelSerializer): title = serializers.JSONField() description = serializers.JSONField() class Meta: model = HadisStatus fields = ["id", "title", "slug", "color", "main_color_code", "order", "description"] class AdminHadisTagSerializer(serializers.ModelSerializer): title = serializers.JSONField() class Meta: model = HadisTag fields = ["id", "title", "status"] class AdminHadisListSerializer(serializers.ModelSerializer): category_detail = AdminHadisCategorySerializer(source="category", read_only=True) hadis_status_detail = AdminHadisStatusSerializer(source="hadis_status", read_only=True) class Meta: model = Hadis fields = [ "id", "number", "slug", "title", "title_narrator", "text", "translation", "status", "category", "category_detail", "hadis_status", "hadis_status_detail", "created_at", "updated_at", ] read_only_fields = ["id", "slug", "created_at", "updated_at"] class HadisCollectionsField(serializers.PrimaryKeyRelatedField): def to_representation(self, value): return value.collection_id class AdminHadisCollectionSerializer(serializers.ModelSerializer): title = serializers.JSONField() summary = serializers.JSONField() thumbnail = AbsoluteImageField(required=False, allow_null=True) remove_thumbnail = serializers.BooleanField(write_only=True, required=False, default=False) class Meta: model = HadisCollection fields = [ "id", "title", "slug", "summary", "status", "pin_top", "order", "thumbnail", "remove_thumbnail", "created_at", "updated_at", ] read_only_fields = ["id", "created_at", "updated_at"] def to_internal_value(self, data): mutable_data = data.copy() if hasattr(data, "copy") else data thumbnail = mutable_data.get("thumbnail") if hasattr(mutable_data, "get") else None if thumbnail and hasattr(thumbnail, "read"): compressed_bytes = maybe_compress_uploaded_file(thumbnail) if compressed_bytes is not None: mutable_data = mutable_data.copy() if hasattr(mutable_data, "copy") else mutable_data mutable_data["thumbnail"] = SimpleUploadedFile( name=getattr(thumbnail, "name", "image"), content=compressed_bytes, content_type=getattr(thumbnail, "content_type", None), ) return super().to_internal_value(mutable_data) def create(self, validated_data): validated_data.pop("remove_thumbnail", False) return super().create(validated_data) def update(self, instance, validated_data): remove_thumbnail = validated_data.pop("remove_thumbnail", False) if remove_thumbnail and instance.thumbnail: instance.thumbnail.delete(save=False) instance.thumbnail = None return super().update(instance, validated_data) class AdminHadisDetailSerializer(serializers.ModelSerializer): category_detail = AdminHadisCategorySerializer(source="category", read_only=True) hadis_status_detail = AdminHadisStatusSerializer(source="hadis_status", read_only=True) tags_detail = AdminHadisTagSerializer(source="tags", many=True, read_only=True) category = serializers.PrimaryKeyRelatedField( queryset=HadisCategory.objects.all(), required=False, allow_null=True, ) hadis_status = serializers.PrimaryKeyRelatedField( queryset=HadisStatus.objects.all(), required=False, allow_null=True, ) tags = serializers.PrimaryKeyRelatedField( queryset=HadisTag.objects.all(), many=True, required=False, ) collections = HadisCollectionsField( source="collection_items", queryset=HadisCollection.objects.all(), many=True, required=False, ) collections_detail = serializers.SerializerMethodField() bookmark_count = serializers.SerializerMethodField() class Meta: model = Hadis fields = [ "id", "number", "slug", "category", "category_detail", "title_narrator", "title", "description", "text", "translation", "status", "hadis_status", "hadis_status_detail", "hadis_status_text", "address", "links", "tags", "tags_detail", "collections", "collections_detail", "share_link", "explanation", "bookmark_count", "created_at", "updated_at", "embedded_in", ] read_only_fields = ["id", "slug", "share_link", "bookmark_count", "created_at", "updated_at"] def get_bookmark_count(self, obj): from apps.bookmark.models.bookmark import Bookmark return Bookmark.objects.filter(service=Bookmark.ServiceChoices.HADITH, content_id=obj.id, status=True).count() def get_collections_detail(self, obj): collections = [item.collection for item in obj.collection_items.all()] return AdminHadisCollectionSerializer(collections, many=True).data def create(self, validated_data): tags = validated_data.pop("tags", []) collections = validated_data.pop("collection_items", []) hadis = super().create(validated_data) if tags: hadis.tags.set(tags) if collections: for idx, col in enumerate(collections): HadisInCollection.objects.create(hadis=hadis, collection=col, order=idx) return hadis def update(self, instance, validated_data): tags = validated_data.pop("tags", None) collections = validated_data.pop("collection_items", None) hadis = super().update(instance, validated_data) if tags is not None: hadis.tags.set(tags) if collections is not None: instance.collection_items.all().delete() for idx, col in enumerate(collections): HadisInCollection.objects.create(hadis=hadis, collection=col, order=idx) return hadis class AdminHadisBriefSerializer(serializers.ModelSerializer): class Meta: model = Hadis fields = ["id", "number", "title"] class AdminHadisCategoryBriefSerializer(serializers.ModelSerializer): title = LocalizedField() class Meta: model = HadisCategory fields = ["id", "title"] def save_relation_reference_data(instance, validated_data, request=None, is_correction=True, ref_type=None): book_reference = validated_data.pop("book_reference_id", None) address = validated_data.pop("address", None) delete_image_ids = validated_data.pop("delete_image_ids", []) new_images = request.FILES.getlist("uploaded_images") if request else [] ref = instance.references.first() if not ref: if book_reference or address or new_images: if ref_type == "original_text": ref = OriginalTextReference.objects.create(original_text=instance) elif is_correction: ref = CorrectionReference.objects.create(correction=instance) else: ref = InterpretationReference.objects.create(interpretation=instance) else: return if book_reference is not None: ref.book_reference = book_reference if address is not None: ref.address = address ref.save() if delete_image_ids: ref.images.filter(id__in=delete_image_ids).delete() for img in new_images: if ref_type == "original_text": OriginalTextReferenceImage.objects.create(reference=ref, image=img) elif is_correction: CorrectionReferenceImage.objects.create(reference=ref, image=img) else: InterpretationReferenceImage.objects.create(reference=ref, image=img) class AdminHadisCorrectionSerializer(serializers.ModelSerializer): hadis_detail = AdminHadisBriefSerializer(source="hadis", read_only=True) bookmark_count = serializers.IntegerField(source="annotated_bookmark_count", read_only=True) # Writable relation fields book_reference_id = serializers.PrimaryKeyRelatedField( queryset=BookReference.objects.all(), required=False, write_only=True, allow_null=True ) address = serializers.CharField( required=False, write_only=True, allow_null=True, allow_blank=True ) delete_image_ids = serializers.ListField( child=serializers.IntegerField(), required=False, write_only=True ) class Meta: model = HadisCorrection fields = [ "id", "hadis", "hadis_detail", "title", "slug", "text", "translation", "priority", "bookmark_count", "links", "book_reference_id", "address", "delete_image_ids", "created_at", "updated_at", ] read_only_fields = ["id", "slug", "bookmark_count", "created_at", "updated_at"] def to_internal_value(self, data): import json data = data.copy() if hasattr(data, "copy") else dict(data) for json_field in ["title", "translation", "links"]: val = data.get(json_field) if isinstance(val, str): try: data[json_field] = json.loads(val) except ValueError: pass delete_ids = data.get("delete_image_ids") if delete_ids: if isinstance(delete_ids, str): try: data["delete_image_ids"] = json.loads(delete_ids) except ValueError: data["delete_image_ids"] = [int(x) for x in delete_ids.split(",") if x.strip().isdigit()] elif isinstance(delete_ids, list): parsed = [] for x in delete_ids: if isinstance(x, str) and x.strip().isdigit(): parsed.append(int(x)) elif isinstance(x, int): parsed.append(x) data["delete_image_ids"] = parsed return super().to_internal_value(data) def to_representation(self, instance): ret = super().to_representation(instance) ref = instance.references.first() ret["book_reference"] = ref.book_reference_id if ref else None ret["address"] = ref.address if ref else "" request = self.context.get("request") ret["images"] = [ { "id": img.id, "image": request.build_absolute_uri(img.image.url) if request and img.image else img.image.url } for img in (ref.images.all() if ref else []) ] return ret def create(self, validated_data): request = self.context.get("request") book_reference_id = validated_data.pop("book_reference_id", None) address = validated_data.pop("address", None) delete_image_ids = validated_data.pop("delete_image_ids", []) instance = super().create(validated_data) ref_data = { "book_reference_id": book_reference_id, "address": address, "delete_image_ids": delete_image_ids, } save_relation_reference_data(instance, ref_data, request=request, is_correction=True) return instance def update(self, instance, validated_data): request = self.context.get("request") book_reference_id = validated_data.pop("book_reference_id", None) address = validated_data.pop("address", None) delete_image_ids = validated_data.pop("delete_image_ids", []) instance = super().update(instance, validated_data) ref_data = { "book_reference_id": book_reference_id, "address": address, "delete_image_ids": delete_image_ids, } save_relation_reference_data(instance, ref_data, request=request, is_correction=True) return instance class AdminHadisInterpretationSerializer(serializers.ModelSerializer): category_detail = AdminHadisCategoryBriefSerializer(source="category", read_only=True) # Writable relation fields book_reference_id = serializers.PrimaryKeyRelatedField( queryset=BookReference.objects.all(), required=False, write_only=True, allow_null=True ) address = serializers.CharField( required=False, write_only=True, allow_null=True, allow_blank=True ) delete_image_ids = serializers.ListField( child=serializers.IntegerField(), required=False, write_only=True ) class Meta: model = HadisInterpretation fields = [ "id", "category", "category_detail", "title", "text", "translation", "priority", "links", "book_reference_id", "address", "delete_image_ids", "created_at", "updated_at", ] read_only_fields = ["id", "created_at", "updated_at"] def to_internal_value(self, data): import json data = data.copy() if hasattr(data, "copy") else dict(data) for json_field in ["title", "translation", "links"]: val = data.get(json_field) if isinstance(val, str): try: data[json_field] = json.loads(val) except ValueError: pass delete_ids = data.get("delete_image_ids") if delete_ids: if isinstance(delete_ids, str): try: data["delete_image_ids"] = json.loads(delete_ids) except ValueError: data["delete_image_ids"] = [int(x) for x in delete_ids.split(",") if x.strip().isdigit()] elif isinstance(delete_ids, list): parsed = [] for x in delete_ids: if isinstance(x, str) and x.strip().isdigit(): parsed.append(int(x)) elif isinstance(x, int): parsed.append(x) data["delete_image_ids"] = parsed return super().to_internal_value(data) def to_representation(self, instance): ret = super().to_representation(instance) ref = instance.references.first() ret["book_reference"] = ref.book_reference_id if ref else None ret["address"] = ref.address if ref else "" request = self.context.get("request") ret["images"] = [ { "id": img.id, "image": request.build_absolute_uri(img.image.url) if request and img.image else img.image.url } for img in (ref.images.all() if ref else []) ] return ret def create(self, validated_data): request = self.context.get("request") book_reference_id = validated_data.pop("book_reference_id", None) address = validated_data.pop("address", None) delete_image_ids = validated_data.pop("delete_image_ids", []) instance = super().create(validated_data) ref_data = { "book_reference_id": book_reference_id, "address": address, "delete_image_ids": delete_image_ids, } save_relation_reference_data(instance, ref_data, request=request, is_correction=False) return instance def update(self, instance, validated_data): request = self.context.get("request") book_reference_id = validated_data.pop("book_reference_id", None) address = validated_data.pop("address", None) delete_image_ids = validated_data.pop("delete_image_ids", []) instance = super().update(instance, validated_data) ref_data = { "book_reference_id": book_reference_id, "address": address, "delete_image_ids": delete_image_ids, } save_relation_reference_data(instance, ref_data, request=request, is_correction=False) return instance class AbsoluteFileField(serializers.FileField): def to_representation(self, value): if not value: return None request = self.context.get("request") url = value.url if hasattr(value, "url") else str(value) if request: return request.build_absolute_uri(url) return url class AdminHadisSectArchiveSerializer(serializers.ModelSerializer): title = serializers.JSONField() description = serializers.JSONField() class Meta: model = HadisSect fields = ["id", "sect_type", "title", "description", "is_active", "order"] class AdminHadisCategoryArchiveParentSerializer(serializers.ModelSerializer): title = serializers.JSONField() class Meta: model = HadisCategory fields = ["id", "title", "slug"] class AdminHadisCategoryArchiveSerializer(serializers.ModelSerializer): parent_detail = AdminHadisCategoryArchiveParentSerializer(source="parent", read_only=True) sect_detail = AdminHadisSectArchiveSerializer(source="sect", read_only=True) title = serializers.JSONField() description = serializers.JSONField() xmind_file = AbsoluteFileField(required=False, allow_null=True) remove_xmind_file = serializers.BooleanField(write_only=True, required=False, default=False) parent = serializers.PrimaryKeyRelatedField( queryset=HadisCategory.objects.all(), required=False, allow_null=True, ) sect = serializers.PrimaryKeyRelatedField( queryset=HadisSect.objects.all(), required=True, ) class Meta: model = HadisCategory fields = [ "id", "parent", "parent_detail", "sect", "sect_detail", "source_type", "title", "description", "order", "xmind_file", "remove_xmind_file", "slug", "share_link", "xmind_share_link", ] read_only_fields = ["id", "share_link", "xmind_share_link"] def create(self, validated_data): validated_data.pop("remove_xmind_file", False) return super().create(validated_data) def update(self, instance, validated_data): remove_xmind_file = validated_data.pop("remove_xmind_file", False) if remove_xmind_file and instance.xmind_file: instance.xmind_file.delete(save=False) instance.xmind_file = None return super().update(instance, validated_data) class AdminTransmitterReliabilitySerializer(serializers.ModelSerializer): class Meta: model = TransmitterReliability fields = ["id", "title", "slug", "color", "main_color_code"] class AdminNarratorLayerSerializer(serializers.ModelSerializer): name = serializers.JSONField() description = serializers.JSONField() class Meta: model = NarratorLayer fields = ["id", "name", "number", "description", "slug", "created_at", "updated_at"] read_only_fields = ["id", "created_at", "updated_at"] class AdminOpinionStatusSerializer(serializers.ModelSerializer): title = serializers.JSONField() class Meta: model = OpinionStatus fields = ["id", "title", "slug", "color", "main_color_code"] class AdminTransmitterListSerializer(serializers.ModelSerializer): reliability_detail = AdminTransmitterReliabilitySerializer(source="reliability", read_only=True) full_name = serializers.JSONField() known_as = serializers.JSONField() nickname = serializers.JSONField() thumbnail = AbsoluteImageField(required=False, allow_null=True) share_link = serializers.CharField(read_only=True) class Meta: model = Transmitters fields = [ "id", "full_name", "slug", "birth_year_hijri", "death_year_hijri", "known_as", "nickname", "reliability", "reliability_detail", "madhhab", "generation", "thumbnail", "share_link", "created_at", "updated_at", ] read_only_fields = ["id", "slug", "share_link", "created_at", "updated_at"] class AdminTransmitterDetailSerializer(serializers.ModelSerializer): reliability_detail = AdminTransmitterReliabilitySerializer(source="reliability", read_only=True) full_name = serializers.JSONField() kunya = serializers.JSONField() known_as = serializers.JSONField() nickname = serializers.JSONField() origin = serializers.JSONField() lived_in = serializers.JSONField() died_in = serializers.JSONField() description = serializers.JSONField() thumbnail = AbsoluteImageField(required=False, allow_null=True) remove_thumbnail = serializers.BooleanField(write_only=True, required=False, default=False) reliability = serializers.PrimaryKeyRelatedField( queryset=TransmitterReliability.objects.all(), required=False, ) share_link = serializers.CharField(read_only=True) class Meta: model = Transmitters fields = [ "id", "full_name", "kunya", "known_as", "nickname", "slug", "legacy_number", "legacy_id", "birth_year_hijri", "death_year_hijri", "age_at_death", "generation", "reliability", "reliability_detail", "madhhab", "in_sahih_muslim", "in_sahih_bukhari", "origin", "lived_in", "died_in", "description", "thumbnail", "remove_thumbnail", "share_link", "created_at", "updated_at", ] read_only_fields = ["id", "slug", "share_link", "created_at", "updated_at", "reliability_detail"] def update(self, instance, validated_data): remove_thumbnail = validated_data.pop("remove_thumbnail", False) if remove_thumbnail and instance.thumbnail: instance.thumbnail.delete(save=False) instance.thumbnail = None return super().update(instance, validated_data) class AdminBookSubjectAreaSerializer(serializers.ModelSerializer): class Meta: model = BookSubjectArea fields = ["id", "title", "created_at", "updated_at"] read_only_fields = ["id", "created_at", "updated_at"] class AdminBookTypeSerializer(serializers.ModelSerializer): class Meta: model = BookType fields = ["id", "title", "created_at", "updated_at"] read_only_fields = ["id", "created_at", "updated_at"] class AdminBookAuthorSerializer(serializers.ModelSerializer): class Meta: model = BookAuthor fields = [ "id", "name", "slug", "birth_year_hijri", "death_year_hijri", "birth_year_miladi", "death_year_miladi", "related_narrator", "known_as", "scholarity", "tabaqa", "rutba", "features", "book_references", ] read_only_fields = ["id", "slug"] class AdminBookAttributeSerializer(serializers.ModelSerializer): class Meta: model = BookAttribute fields = ["id", "book_reference", "title", "value"] class AdminBookReferenceImageSerializer(serializers.ModelSerializer): image = AbsoluteImageField(required=True) volume = serializers.SerializerMethodField() class Meta: model = BookReferenceImage fields = ["id", "book_reference", "image", "volume", "order", "description"] def get_volume(self, obj): return obj.book_volume.title if getattr(obj, 'book_volume', None) else "" class AdminBookReferenceDocumentSerializer(serializers.ModelSerializer): file = AbsoluteFileField(required=True) volume = serializers.SerializerMethodField() class Meta: model = BookReferenceDocument fields = ["id", "book_reference", "file", "volume", "title", "order"] def get_volume(self, obj): return obj.book_volume.title if getattr(obj, 'book_volume', None) else "" class AdminBookReferenceListSerializer(serializers.ModelSerializer): volume = serializers.SerializerMethodField() publisher = serializers.SerializerMethodField() year_of_publication = serializers.SerializerMethodField() class Meta: model = BookReference fields = [ "id", "title", "slug", "volume", "publisher", "year_of_publication", "rate", "created_at", ] def get_volume(self, obj): vols = list(obj.volumes.all()) return vols[0].title if vols else "" def get_publisher(self, obj): editions = list(obj.editions.all()) return editions[0].publisher if editions else [] def get_year_of_publication(self, obj): editions = list(obj.editions.all()) return editions[0].year_of_publication if editions else "" class AdminBookReferenceDetailSerializer(serializers.ModelSerializer): subject_area_detail = AdminBookSubjectAreaSerializer(source="subject_area", many=True, read_only=True) type_detail = AdminBookTypeSerializer(source="type", read_only=True) authors_detail = AdminBookAuthorSerializer(source="authors", many=True, read_only=True) subject_area = serializers.PrimaryKeyRelatedField( queryset=BookSubjectArea.objects.all(), many=True, required=False, ) type = serializers.PrimaryKeyRelatedField( queryset=BookType.objects.all(), required=False, allow_null=True, ) authors = serializers.PrimaryKeyRelatedField( queryset=BookAuthor.objects.all(), many=True, required=False, ) source_url = serializers.SerializerMethodField() isbn = serializers.SerializerMethodField() volume = serializers.SerializerMethodField() number_of_volumes = serializers.SerializerMethodField() year_of_publication = serializers.SerializerMethodField() publisher = serializers.SerializerMethodField() city_of_publication = serializers.SerializerMethodField() country_of_publication = serializers.SerializerMethodField() edition_number = serializers.SerializerMethodField() def get_source_url(self, obj): ed = obj.editions.first() return ed.source_url if ed else "" def get_isbn(self, obj): ed = obj.editions.first() return ed.isbn if ed else "" def get_volume(self, obj): vol = obj.volumes.first() return vol.title if vol else "" def get_number_of_volumes(self, obj): ed = obj.editions.first() return ed.number_of_volumes if ed else None def get_year_of_publication(self, obj): ed = obj.editions.first() return ed.year_of_publication if ed else "" def get_publisher(self, obj): ed = obj.editions.first() return ed.publisher if ed else [] def get_city_of_publication(self, obj): ed = obj.editions.first() return ed.city_of_publication if ed else [] def get_country_of_publication(self, obj): ed = obj.editions.first() return ed.country_of_publication if ed else [] def get_edition_number(self, obj): ed = obj.editions.first() return ed.edition_number if ed else "" class Meta: model = BookReference fields = [ "id", "legacy_id", "source_url", "title", "description", "language", "isbn", "volume", "number_of_volumes", "year_of_publication", "number_page", "slug", "publisher", "subject_area", "subject_area_detail", "type", "type_detail", "order", "researcher", "city_of_publication", "country_of_publication", "edition_number", "rate", "authors", "authors_detail", "created_at", "updated_at", "share_link", ] read_only_fields = ["id", "slug", "created_at", "updated_at", "share_link"] def create(self, validated_data): subject_area = validated_data.pop("subject_area", []) authors = validated_data.pop("authors", []) book = super().create(validated_data) if subject_area: book.subject_area.set(subject_area) if authors: book.authors.set(authors) return book def update(self, instance, validated_data): subject_area = validated_data.pop("subject_area", None) authors = validated_data.pop("authors", None) book = super().update(instance, validated_data) if subject_area is not None: book.subject_area.set(subject_area) if authors is not None: book.authors.set(authors) return book # Category Quran Verse Admin Serializers from ..models.category import CategoryQuranVerse, QuranSecondaryTranslation class AdminQuranSecondaryTranslationSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) translation_text = serializers.JSONField(default=list) translator = serializers.JSONField(default=list) class Meta: model = QuranSecondaryTranslation fields = ["id", "translation_text", "translator"] class AdminCategoryQuranVerseSerializer(serializers.ModelSerializer): surah_name = serializers.JSONField(default=list) translation = serializers.JSONField(default=list) translator = serializers.JSONField(default=list) secondary_translations = AdminQuranSecondaryTranslationSerializer(many=True, required=False) class Meta: model = CategoryQuranVerse fields = [ "id", "category", "surah_name", "ayah_number", "arabic_text", "translation", "translator", "secondary_translations", ] def create(self, validated_data): secondary_data = validated_data.pop("secondary_translations", []) verse = CategoryQuranVerse.objects.create(**validated_data) for sec in secondary_data: QuranSecondaryTranslation.objects.create(quran_verse=verse, **sec) return verse def update(self, instance, validated_data): secondary_data = validated_data.pop("secondary_translations", None) instance.surah_name = validated_data.get("surah_name", instance.surah_name) instance.ayah_number = validated_data.get("ayah_number", instance.ayah_number) instance.arabic_text = validated_data.get("arabic_text", instance.arabic_text) instance.translation = validated_data.get("translation", instance.translation) instance.translator = validated_data.get("translator", instance.translator) instance.category = validated_data.get("category", instance.category) instance.save() if secondary_data is not None: keep_ids = [item.get("id") for item in secondary_data if item.get("id")] instance.secondary_translations.exclude(id__in=keep_ids).delete() for sec in secondary_data: sec_id = sec.get("id") if sec_id: QuranSecondaryTranslation.objects.filter(id=sec_id, quran_verse=instance).update( translation_text=sec.get("translation_text", []), translator=sec.get("translator", []) ) else: QuranSecondaryTranslation.objects.create(quran_verse=instance, **sec) return instance class AdminHadisTransmitterSerializer(serializers.ModelSerializer): class Meta: model = HadisTransmitter fields = [ "id", "hadis", "transmitter", "chain_index", "narrator_layer", "status", "order", "is_gap", ] class AdminTransmitterOpinionSerializer(serializers.ModelSerializer): scholar_name = serializers.JSONField(required=False, default=list) opinion_text = serializers.JSONField(required=False, default=list) status_detail = AdminTransmitterReliabilitySerializer(source="status", read_only=True) class Meta: model = TransmitterOpinion fields = [ "id", "transmitter", "scholar", "scholar_name", "opinion_text", "status", "status_detail", "order", "created_at", "updated_at", ] class AdminTransmitterOriginalTextSerializer(serializers.ModelSerializer): title = serializers.JSONField(required=False, default=list) text = serializers.JSONField(required=False, default=list) translation = serializers.JSONField(required=False, default=list) # Writable relation fields book_reference_id = serializers.PrimaryKeyRelatedField( queryset=BookReference.objects.all(), required=False, write_only=True, allow_null=True ) address = serializers.CharField( required=False, write_only=True, allow_null=True, allow_blank=True ) delete_image_ids = serializers.ListField( child=serializers.IntegerField(), required=False, write_only=True ) class Meta: model = TransmitterOriginalText fields = [ "id", "transmitter", "slug", "title", "text", "translation", "share_link", "embedded_in", "order", "book_reference_id", "address", "delete_image_ids", ] def to_internal_value(self, data): import json data = data.copy() if hasattr(data, "copy") else dict(data) for json_field in ["title", "text", "translation"]: val = data.get(json_field) if isinstance(val, str): try: data[json_field] = json.loads(val) except ValueError: pass delete_ids = data.get("delete_image_ids") if delete_ids: if isinstance(delete_ids, str): try: data["delete_image_ids"] = json.loads(delete_ids) except ValueError: data["delete_image_ids"] = [int(x) for x in delete_ids.split(",") if x.strip().isdigit()] elif isinstance(delete_ids, list): parsed = [] for x in delete_ids: if isinstance(x, str) and x.strip().isdigit(): parsed.append(int(x)) elif isinstance(x, int): parsed.append(x) data["delete_image_ids"] = parsed return super().to_internal_value(data) def to_representation(self, instance): ret = super().to_representation(instance) ref = instance.references.first() ret["book_reference"] = ref.book_reference_id if ref else None ret["address"] = ref.address if ref else "" request = self.context.get("request") ret["images"] = [ { "id": img.id, "image": request.build_absolute_uri(img.image.url) if request and img.image else img.image.url } for img in (ref.images.all() if ref else []) ] return ret def create(self, validated_data): request = self.context.get("request") book_reference_id = validated_data.pop("book_reference_id", None) address = validated_data.pop("address", None) delete_image_ids = validated_data.pop("delete_image_ids", []) instance = super().create(validated_data) ref_data = { "book_reference_id": book_reference_id, "address": address, "delete_image_ids": delete_image_ids, } save_relation_reference_data(instance, ref_data, request=request, ref_type="original_text") return instance def update(self, instance, validated_data): request = self.context.get("request") book_reference_id = validated_data.pop("book_reference_id", None) address = validated_data.pop("address", None) delete_image_ids = validated_data.pop("delete_image_ids", []) instance = super().update(instance, validated_data) ref_data = { "book_reference_id": book_reference_id, "address": address, "delete_image_ids": delete_image_ids, } save_relation_reference_data(instance, ref_data, request=request, ref_type="original_text") return instance class AdminBookVolumeSerializer(serializers.ModelSerializer): class Meta: model = BookVolume fields = [ "id", "book_reference", "edition", "title", "created_at", "updated_at", ] class AdminBookEditionSerializer(serializers.ModelSerializer): publisher = serializers.JSONField(required=False, default=list) city_of_publication = serializers.JSONField(required=False, default=list) country_of_publication = serializers.JSONField(required=False, default=list) notes = serializers.JSONField(required=False, default=list) tags = serializers.JSONField(required=False, default=list) volumes_detail = AdminBookVolumeSerializer(source="volumes", many=True, read_only=True) number_of_volumes = serializers.SerializerMethodField() class Meta: model = BookEdition fields = [ "id", "book_reference", "publisher", "city_of_publication", "country_of_publication", "edition_number", "year_of_publication", "number_of_volumes", "source_url", "isbn", "notes", "tags", "volumes_detail", "created_at", "updated_at", ] def get_number_of_volumes(self, obj): return obj.volumes.count()