From 7f053d86f9c806c1c998f5150b4439ac7fc13166 Mon Sep 17 00:00:00 2001 From: mohsentaba Date: Tue, 7 Jul 2026 12:21:07 +0330 Subject: [PATCH] interpretations and corrections detail modal api added --- apps/hadis/serializers/serializers_admin.py | 232 +++++++++++++++++++- apps/hadis/views_admin.py | 4 +- 2 files changed, 233 insertions(+), 3 deletions(-) diff --git a/apps/hadis/serializers/serializers_admin.py b/apps/hadis/serializers/serializers_admin.py index e541ac0..432902c 100644 --- a/apps/hadis/serializers/serializers_admin.py +++ b/apps/hadis/serializers/serializers_admin.py @@ -5,7 +5,8 @@ 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 + Transmitters, TransmitterReliability, NarratorLayer, OpinionStatus, TransmitterOpinion, TransmitterOriginalText, + CorrectionReference, CorrectionReferenceImage, InterpretationReference, InterpretationReferenceImage ) from .category import LocalizedField @@ -244,10 +245,62 @@ class AdminHadisCategoryBriefSerializer(serializers.ModelSerializer): fields = ["id", "title"] +def save_relation_reference_data(instance, validated_data, request=None, is_correction=True): + 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 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 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 = [ @@ -260,15 +313,113 @@ class AdminHadisCorrectionSerializer(serializers.ModelSerializer): "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 = [ @@ -279,11 +430,90 @@ class AdminHadisInterpretationSerializer(serializers.ModelSerializer): "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: diff --git a/apps/hadis/views_admin.py b/apps/hadis/views_admin.py index 1b335d7..55617dc 100644 --- a/apps/hadis/views_admin.py +++ b/apps/hadis/views_admin.py @@ -329,7 +329,7 @@ class AdminHadisCorrectionViewSet(ModelViewSet): status=True ).values("content_id").annotate(count=Count("id")).values("count") - queryset = HadisCorrection.objects.all().select_related("hadis").annotate( + queryset = HadisCorrection.objects.all().select_related("hadis").prefetch_related("references", "references__images").annotate( annotated_bookmark_count=Coalesce(Subquery(bookmarks_subquery, output_field=IntegerField()), 0) ) hadis_id = self.request.query_params.get("hadis_id") @@ -365,7 +365,7 @@ class AdminHadisInterpretationViewSet(ModelViewSet): pagination_class = None def get_queryset(self): - queryset = HadisInterpretation.objects.all().select_related("category") + queryset = HadisInterpretation.objects.all().select_related("category").prefetch_related("references", "references__images") category_id = self.request.query_params.get("category_id") if category_id: queryset = queryset.filter(category_id=category_id)