From 76575da48128a00d5348b9ab5360c2b9071e0546 Mon Sep 17 00:00:00 2001 From: mohsentaba Date: Tue, 6 Oct 2026 11:45:03 +0330 Subject: [PATCH] feat(hadis): support multiple book references with scan images, volume details, and admin endpoints --- ...ess_hadisreference_book_volume_and_more.py | 29 ++ apps/hadis/models/hadis.py | 17 + apps/hadis/serializers/hadis.py | 203 ++++++++++- apps/hadis/serializers/serializers_admin.py | 314 +++++++++++++++++- apps/hadis/urls.py | 3 +- apps/hadis/views/hadis.py | 4 +- apps/hadis/views_admin.py | 18 + 7 files changed, 570 insertions(+), 18 deletions(-) create mode 100644 apps/hadis/migrations/0045_hadisreference_address_hadisreference_book_volume_and_more.py diff --git a/apps/hadis/migrations/0045_hadisreference_address_hadisreference_book_volume_and_more.py b/apps/hadis/migrations/0045_hadisreference_address_hadisreference_book_volume_and_more.py new file mode 100644 index 0000000..68c6a20 --- /dev/null +++ b/apps/hadis/migrations/0045_hadisreference_address_hadisreference_book_volume_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 4.2.30 on 2026-10-06 10:04 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('hadis', '0044_alter_hadiscategory_description_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='hadisreference', + name='address', + field=models.TextField(blank=True, null=True, verbose_name='Address'), + ), + migrations.AddField( + model_name='hadisreference', + name='book_volume', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='hadis_references', to='hadis.bookvolume', verbose_name='Volume Record'), + ), + migrations.AddField( + model_name='hadisreference', + name='edition', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='hadis_references', to='hadis.bookedition', verbose_name='Edition'), + ), + ] diff --git a/apps/hadis/models/hadis.py b/apps/hadis/models/hadis.py index 3985f41..b9a93a8 100644 --- a/apps/hadis/models/hadis.py +++ b/apps/hadis/models/hadis.py @@ -307,8 +307,25 @@ class HadisReference(models.Model): verbose_name=_('book reference'), related_name='hadis_references' ) + edition = models.ForeignKey( + 'hadis.BookEdition', + on_delete=models.SET_NULL, + null=True, + blank=True, + verbose_name=_('Edition'), + related_name='hadis_references' + ) + book_volume = models.ForeignKey( + 'hadis.BookVolume', + on_delete=models.SET_NULL, + null=True, + blank=True, + verbose_name=_('Volume Record'), + related_name='hadis_references' + ) volume = models.CharField(max_length=100, null=True, blank=True) pages = models.CharField(max_length=100, null=True, blank=True) + address = models.TextField(null=True, blank=True, verbose_name=_('Address')) url = models.URLField(max_length=1000, null=True, blank=True) hadith_number = models.CharField(max_length=100, null=True, blank=True, verbose_name=_('Hadith Number in Book')) diff --git a/apps/hadis/serializers/hadis.py b/apps/hadis/serializers/hadis.py index 0a56614..240ce2a 100644 --- a/apps/hadis/serializers/hadis.py +++ b/apps/hadis/serializers/hadis.py @@ -1036,33 +1036,181 @@ class HadisTransmitterListSerializer(serializers.ModelSerializer): class ReferenceImageSerializer(serializers.ModelSerializer): """Serializer for ReferenceImage""" thumbnail = serializers.SerializerMethodField() + image = serializers.SerializerMethodField() class Meta: model = ReferenceImage - fields = ['id', 'thumbnail', 'priority'] + fields = ['id', 'thumbnail', 'image', 'priority'] def get_thumbnail(self, obj): """Get thumbnail URL""" - if obj.thumbnail: + img = getattr(obj, 'thumbnail', None) or getattr(obj, 'image', None) + if img: request = self.context.get('request') if request: - return absolute_https_url(obj.thumbnail.url, request) - return absolute_https_url(obj.thumbnail.url) + return absolute_https_url(img.url, request) + return absolute_https_url(img.url) return None + def get_image(self, obj): + return self.get_thumbnail(obj) + class HadisReferenceSerializer(serializers.ModelSerializer): - """Serializer for HadisReference with book and images""" + """Serializer for HadisReference with book, author, edition, volume, 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) + edition = serializers.SerializerMethodField() + edition_info = serializers.SerializerMethodField() + volume_info = serializers.SerializerMethodField() + volume = serializers.SerializerMethodField() + pages = serializers.SerializerMethodField() + hadith_number = serializers.SerializerMethodField() + citation_summary = serializers.SerializerMethodField() + open_book_url = serializers.SerializerMethodField() + book = serializers.SerializerMethodField() + author = serializers.SerializerMethodField() + images = ReferenceImageSerializer(many=True, read_only=True) + images_count = serializers.SerializerMethodField() + class Meta: model = HadisReference fields = [ - 'id', 'book_title', 'book_description','book_authors','share_link' + 'id', 'book', 'author', 'book_title', 'book_description', 'book_authors', 'share_link', + 'edition', 'edition_info', 'volume_info', 'volume', 'pages', 'hadith_number', + 'address', 'url', 'open_book_url', 'citation_summary', 'images', 'images_count' ] + 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 + + def get_edition(self, obj): + return self.get_edition_info(obj) + + 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_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 obj.url or None + + 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 + + return { + 'id': obj.book_reference.id, + 'slug': obj.book_reference.slug, + 'title': get_localized_text(obj.book_reference.title, request) if isinstance(obj.book_reference.title, list) else obj.book_reference.title, + '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) if isinstance(author.name, list) else author.name + } + return None + + 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() + # def get_type_name(self, obj): # """Get localized type name from book_reference.type""" # if obj.book_reference and obj.book_reference.type: @@ -1648,6 +1796,32 @@ class DetailedOriginalTextReferenceSerializer(BaseDetailedReferenceSerializer): fields = ['id', 'book_title', 'book_authors', 'edition_info', 'volume_info', 'volume', 'pages', 'address', 'url', 'images'] +class DetailedHadisReferenceSerializer(DetailedCorrectionReferenceSerializer): + images = ReferenceImageSerializer(many=True, read_only=True) + + class Meta: + model = HadisReference + 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', + ] + + # --- سریالایزرهای اصلی جزئیات (Detail Serializers) --- class HadisCorrectionDetailSerializer(serializers.ModelSerializer): @@ -1779,7 +1953,7 @@ class TransmitterOriginalTextDetailSerializer(serializers.ModelSerializer): class HadisSourceDetailsSerializer(serializers.ModelSerializer): """ - Lightweight serializer for Hadis Source Details (Address, Links, Images). + Lightweight serializer for Hadis Source Details (Address, Links, Images, Reference). Used on-demand when opening source detail modals in web and mobile clients. """ title = LocalizedField() @@ -1788,12 +1962,15 @@ class HadisSourceDetailsSerializer(serializers.ModelSerializer): address = serializers.SerializerMethodField() links = serializers.SerializerMethodField() images = serializers.SerializerMethodField() + reference = serializers.SerializerMethodField() + references = serializers.SerializerMethodField() class Meta: model = Hadis fields = [ 'id', 'slug', 'title', 'narrator', 'source_type', - 'address', 'links', 'images', 'share_link' + 'address', 'links', 'images', 'share_link', + 'reference', 'references' ] def get_source_type(self, obj): @@ -1835,3 +2012,13 @@ class HadisSourceDetailsSerializer(serializers.ModelSerializer): }) return images_list + def get_reference(self, obj): + ref = obj.references.first() + if ref: + return DetailedHadisReferenceSerializer(ref, context=self.context).data + return None + + def get_references(self, obj): + refs = obj.references.all() + return DetailedHadisReferenceSerializer(refs, many=True, context=self.context).data + diff --git a/apps/hadis/serializers/serializers_admin.py b/apps/hadis/serializers/serializers_admin.py index 67a4ade..fdfea3c 100644 --- a/apps/hadis/serializers/serializers_admin.py +++ b/apps/hadis/serializers/serializers_admin.py @@ -245,6 +245,174 @@ class AdminHadisCollectionSerializer(serializers.ModelSerializer): return super().update(instance, validated_data) +class AdminHadisReferenceSerializer(serializers.ModelSerializer): + book_reference_detail = serializers.SerializerMethodField(read_only=True) + edition_detail = serializers.SerializerMethodField(read_only=True) + book_volume_detail = serializers.SerializerMethodField(read_only=True) + images = serializers.SerializerMethodField(read_only=True) + delete_image_ids = serializers.ListField( + child=serializers.IntegerField(), + required=False, + write_only=True + ) + + class Meta: + model = HadisReference + fields = [ + "id", + "hadis", + "book_reference", + "book_reference_detail", + "edition", + "edition_detail", + "book_volume", + "book_volume_detail", + "hadith_number", + "volume", + "pages", + "address", + "url", + "images", + "delete_image_ids", + ] + + def get_book_reference_detail(self, obj): + if obj.book_reference: + request = self.context.get("request") + author = obj.book_reference.author + first_ed = obj.book_reference.editions.first() if hasattr(obj.book_reference, "editions") else None + 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 + return { + "id": obj.book_reference.id, + "title": get_localized_text(obj.book_reference.title, request) if isinstance(obj.book_reference.title, list) else obj.book_reference.title, + "slug": obj.book_reference.slug, + "publisher": pub or "", + "year_of_publication": year or "", + "number_of_volumes": num_vols, + "has_editions": getattr(obj.book_reference, "has_editions", False), + "author": { + "id": author.id, + "name": get_localized_text(author.name, request) if isinstance(author.name, list) else author.name, + "slug": author.slug + } if author else None + } + return None + + def get_edition_detail(self, obj): + if obj.edition: + request = self.context.get("request") + pub = obj.edition.publisher + if isinstance(pub, list): + pub = get_localized_text(pub, request) + return { + "id": obj.edition.id, + "edition_number": obj.edition.edition_number, + "publisher": pub or "", + "year_of_publication": obj.edition.year_of_publication or "", + "number_of_volumes": obj.edition.number_of_volumes, + } + return None + + def get_book_volume_detail(self, obj): + if obj.book_volume: + return { + "id": obj.book_volume.id, + "title": obj.book_volume.title, + } + return None + + def get_images(self, obj): + request = self.context.get("request") + return [ + { + "id": img.id, + "reference_id": obj.id, + "hadis_id": obj.hadis_id, + "thumbnail": absolute_https_url(img.thumbnail.url, request) if request and img.thumbnail else (img.thumbnail.url if img.thumbnail else None), + "image": absolute_https_url(img.thumbnail.url, request) if request and img.thumbnail else (img.thumbnail.url if img.thumbnail else None), + "priority": img.priority, + } + for img in obj.images.all().order_by("priority", "id") + ] + + def to_internal_value(self, data): + import json + data = safe_copy_data(data) + book_ref = data.get("book_reference") + if book_ref in ("", "none", "null"): + data["book_reference"] = None + + edition = data.get("edition") + if edition in ("", "none", "null"): + data["edition"] = None + + book_vol = data.get("book_volume") + if book_vol in ("", "none", "null"): + data["book_volume"] = None + + 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 create(self, validated_data): + delete_image_ids = validated_data.pop("delete_image_ids", []) + instance = super().create(validated_data) + self._handle_images(instance, delete_image_ids) + return instance + + def update(self, instance, validated_data): + delete_image_ids = validated_data.pop("delete_image_ids", []) + instance = super().update(instance, validated_data) + self._handle_images(instance, delete_image_ids) + return instance + + def _handle_images(self, instance, delete_image_ids): + request = self.context.get("request") + if delete_image_ids: + instance.images.filter(id__in=delete_image_ids).delete() + + if request: + new_images = ( + request.FILES.getlist("uploaded_images") + or request.FILES.getlist("uploaded_images[]") + or request.FILES.getlist("images") + or request.FILES.getlist("images[]") + ) + single_img = request.FILES.get("image") or request.FILES.get("thumbnail") + if single_img and not new_images: + new_images = [single_img] + + last_img = instance.images.order_by("-priority").first() + current_priority = (last_img.priority + 1) if last_img else 1 + + for img in new_images: + ReferenceImage.objects.create(reference=instance, thumbnail=img, priority=current_priority) + current_priority += 1 + + class AdminHadisDetailSerializer(serializers.ModelSerializer): category_detail = AdminHadisCategorySerializer(source="category", read_only=True) hadis_status_detail = AdminHadisStatusSerializer(source="hadis_status", read_only=True) @@ -279,6 +447,36 @@ class AdminHadisDetailSerializer(serializers.ModelSerializer): write_only=True, allow_null=True, ) + edition_id = serializers.PrimaryKeyRelatedField( + queryset=BookEdition.objects.all(), + required=False, + write_only=True, + allow_null=True, + ) + book_volume_id = serializers.PrimaryKeyRelatedField( + queryset=BookVolume.objects.all(), + required=False, + write_only=True, + allow_null=True, + ) + volume = serializers.CharField( + required=False, + write_only=True, + allow_null=True, + allow_blank=True, + ) + pages = serializers.CharField( + required=False, + write_only=True, + allow_null=True, + allow_blank=True, + ) + hadith_number = serializers.CharField( + required=False, + write_only=True, + allow_null=True, + allow_blank=True, + ) bookmark_count = serializers.SerializerMethodField() @@ -325,6 +523,11 @@ class AdminHadisDetailSerializer(serializers.ModelSerializer): "collections", "collections_detail", "book_reference_id", + "edition_id", + "book_volume_id", + "volume", + "pages", + "hadith_number", "share_link", "explanation", "bookmark_count", @@ -336,22 +539,75 @@ class AdminHadisDetailSerializer(serializers.ModelSerializer): def to_representation(self, instance): ret = super().to_representation(instance) + request = self.context.get("request") refs = list(instance.references.all()) ref = refs[0] if refs else None + ret["book_reference"] = ref.book_reference_id if ref else None + ret["edition"] = ref.edition_id if ref else None + ret["book_volume"] = ref.book_volume_id if ref else None + ret["volume"] = ref.volume if ref else None + ret["pages"] = ref.pages if ref else None + ret["hadith_number"] = ref.hadith_number if ref else None + if ref and ref.book_reference: + author = ref.book_reference.author + first_ed = ref.book_reference.editions.first() if hasattr(ref.book_reference, "editions") else None + pub = getattr(ref.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(ref.book_reference, "year_of_publication", None) + if not year and first_ed: + year = first_ed.year_of_publication + num_vols = getattr(ref.book_reference, "number_of_volumes", None) + if num_vols is None and first_ed: + num_vols = first_ed.number_of_volumes ret["book_reference_detail"] = { "id": ref.book_reference.id, - "title": ref.book_reference.title, + "title": get_localized_text(ref.book_reference.title, request) if isinstance(ref.book_reference.title, list) else ref.book_reference.title, "slug": ref.book_reference.slug, + "publisher": pub or "", + "year_of_publication": year or "", + "number_of_volumes": num_vols, + "has_editions": getattr(ref.book_reference, "has_editions", False), + "author": { + "id": author.id, + "name": get_localized_text(author.name, request) if isinstance(author.name, list) else author.name, + "slug": author.slug + } if author else None } else: ret["book_reference_detail"] = None + if ref and ref.edition: + ed = ref.edition + pub = ed.publisher + if isinstance(pub, list): + pub = get_localized_text(pub, request) + ret["edition_detail"] = { + "id": ed.id, + "edition_number": ed.edition_number, + "publisher": pub or "", + "year_of_publication": ed.year_of_publication or "", + "number_of_volumes": ed.number_of_volumes, + } + else: + ret["edition_detail"] = None + + if ref and ref.book_volume: + ret["book_volume_detail"] = { + "id": ref.book_volume.id, + "title": ref.book_volume.title, + } + else: + ret["book_volume_detail"] = None + ref_images = [] for r in refs: for img in r.images.all().order_by("priority", "id"): - url = absolute_https_url(img.thumbnail.url, self.context.get("request")) if img.thumbnail else None + url = absolute_https_url(img.thumbnail.url, request) if img.thumbnail else None ref_images.append({ "id": img.id, "reference_id": r.id, @@ -361,6 +617,7 @@ class AdminHadisDetailSerializer(serializers.ModelSerializer): "priority": img.priority, }) ret["reference_images"] = ref_images + ret["references"] = AdminHadisReferenceSerializer(instance.references.all().order_by("id"), many=True, context=self.context).data return ret def get_bookmark_count(self, obj): @@ -380,6 +637,11 @@ class AdminHadisDetailSerializer(serializers.ModelSerializer): def create(self, validated_data): book_reference = validated_data.pop("book_reference_id", None) + edition = validated_data.pop("edition_id", None) + book_volume = validated_data.pop("book_volume_id", None) + volume = validated_data.pop("volume", None) + pages = validated_data.pop("pages", None) + hadith_number = validated_data.pop("hadith_number", None) tags = validated_data.pop("tags", []) collections = validated_data.pop("collection_items", []) hadis = super().create(validated_data) @@ -388,13 +650,32 @@ class AdminHadisDetailSerializer(serializers.ModelSerializer): if collections: for idx, col in enumerate(collections): HadisInCollection.objects.create(hadis=hadis, collection=col, order=idx) - if book_reference: - HadisReference.objects.create(hadis=hadis, book_reference=book_reference) + if book_reference or edition or book_volume or volume or pages or hadith_number: + HadisReference.objects.create( + hadis=hadis, + book_reference=book_reference, + edition=edition, + book_volume=book_volume, + volume=volume or "", + pages=pages or "", + hadith_number=hadith_number or "" + ) return hadis def update(self, instance, validated_data): book_reference_provided = "book_reference_id" in validated_data book_reference = validated_data.pop("book_reference_id", None) + edition_provided = "edition_id" in validated_data + edition = validated_data.pop("edition_id", None) + book_volume_provided = "book_volume_id" in validated_data + book_volume = validated_data.pop("book_volume_id", None) + volume_provided = "volume" in validated_data + volume = validated_data.pop("volume", None) + pages_provided = "pages" in validated_data + pages = validated_data.pop("pages", None) + hadith_number_provided = "hadith_number" in validated_data + hadith_number = validated_data.pop("hadith_number", None) + tags = validated_data.pop("tags", None) collections = validated_data.pop("collection_items", None) hadis = super().update(instance, validated_data) @@ -404,13 +685,30 @@ class AdminHadisDetailSerializer(serializers.ModelSerializer): instance.collection_items.all().delete() for idx, col in enumerate(collections): HadisInCollection.objects.create(hadis=hadis, collection=col, order=idx) - if book_reference_provided: + + any_ref_provided = ( + book_reference_provided or edition_provided or book_volume_provided or + volume_provided or pages_provided or hadith_number_provided + ) + if any_ref_provided: ref = instance.references.first() + if not ref: + if book_reference or edition or book_volume or volume or pages or hadith_number: + ref = HadisReference.objects.create(hadis=hadis) if ref: - ref.book_reference = book_reference + if book_reference_provided: + ref.book_reference = book_reference + if edition_provided: + ref.edition = edition + if book_volume_provided: + ref.book_volume = book_volume + if volume_provided: + ref.volume = volume or "" + if pages_provided: + ref.pages = pages or "" + if hadith_number_provided: + ref.hadith_number = hadith_number or "" ref.save() - elif book_reference: - HadisReference.objects.create(hadis=hadis, book_reference=book_reference) return hadis diff --git a/apps/hadis/urls.py b/apps/hadis/urls.py index a32def2..f26e2db 100644 --- a/apps/hadis/urls.py +++ b/apps/hadis/urls.py @@ -14,7 +14,7 @@ from .views.reference_v2 import BookReferenceV2DetailView, BookReferenceV2SyncVi from .views.version import ContentReleaseSyncView, HadisDatabaseVersionView from .views.info import HadisInfoView from .views_admin import ( - AdminHadisCategoryViewSet, AdminHadisViewSet, AdminHadisCorrectionViewSet, AdminCorrectionReferenceViewSet, AdminHadisInterpretationViewSet, AdminHadisStatusViewSet, AdminHadisTagViewSet, AdminHadisCollectionViewSet, + AdminHadisCategoryViewSet, AdminHadisViewSet, AdminHadisCorrectionViewSet, AdminCorrectionReferenceViewSet, AdminHadisReferenceViewSet, AdminHadisInterpretationViewSet, AdminHadisStatusViewSet, AdminHadisTagViewSet, AdminHadisCollectionViewSet, AdminBookSubjectAreaViewSet, AdminBookTypeViewSet, AdminBookTagViewSet, AdminBookAuthorViewSet, AdminBookAttributeViewSet, AdminBookReferenceImageViewSet, AdminHadisReferenceImageViewSet, AdminBookReferenceDocumentViewSet, AdminBookReferenceViewSet, AdminTransmitterViewSet, AdminTransmitterReliabilityViewSet, DovodiBookSubjectAreaViewSet, DovodiBookTypeViewSet, DovodiNarratorLayerViewSet, DovodiTransmitterReliabilityViewSet, DovodiOpinionStatusViewSet, @@ -36,6 +36,7 @@ admin_router.register(r'dovodi-sects', DovodiHadisSectViewSet, basename='dovodi- admin_router.register(r'dovodi-categories', DovodiHadisCategoryViewSet, basename='dovodi-hadis-categories') admin_router.register(r'corrections', AdminHadisCorrectionViewSet, basename='admin-hadis-corrections') admin_router.register(r'correction-references', AdminCorrectionReferenceViewSet, basename='admin-correction-references') +admin_router.register(r'hadis-references', AdminHadisReferenceViewSet, basename='admin-hadis-references') admin_router.register(r'interpretations', AdminHadisInterpretationViewSet, basename='admin-hadis-interpretations') admin_router.register(r'statuses', AdminHadisStatusViewSet, basename='admin-hadis-statuses') admin_router.register(r'tags', AdminHadisTagViewSet, basename='admin-hadis-tags') diff --git a/apps/hadis/views/hadis.py b/apps/hadis/views/hadis.py index a350362..7f83365 100644 --- a/apps/hadis/views/hadis.py +++ b/apps/hadis/views/hadis.py @@ -1323,7 +1323,9 @@ class HadisSourceDetailsView(RetrieveAPIView): ).prefetch_related( Prefetch( 'references', - queryset=HadisReference.objects.prefetch_related( + queryset=HadisReference.objects.select_related( + 'book_reference', 'book_reference__author', 'edition', 'book_volume' + ).prefetch_related( Prefetch('images', queryset=ReferenceImage.objects.order_by('priority')) ) ) diff --git a/apps/hadis/views_admin.py b/apps/hadis/views_admin.py index 1d40073..79edc18 100644 --- a/apps/hadis/views_admin.py +++ b/apps/hadis/views_admin.py @@ -24,6 +24,7 @@ from .serializers.serializers_admin import ( AdminHadisCategoryArchiveDetailSerializer, AdminHadisCorrectionSerializer, AdminCorrectionReferenceSerializer, + AdminHadisReferenceSerializer, AdminHadisDetailSerializer, AdminHadisInterpretationSerializer, AdminHadisCollectionSerializer, @@ -466,6 +467,23 @@ class AdminCorrectionReferenceViewSet(ModelViewSet): return qs.order_by("id") +class AdminHadisReferenceViewSet(ModelViewSet): + serializer_class = AdminHadisReferenceSerializer + permission_classes = [IsAuthenticated, IsSuperAdmin] + authentication_classes = [TokenAuthentication] + parser_classes = (MultiPartParser, FormParser, JSONParser) + pagination_class = OptionalPagination + + def get_queryset(self): + qs = HadisReference.objects.all().select_related( + "book_reference__author", "edition", "book_volume" + ).prefetch_related("images") + hadis_id = self.request.query_params.get("hadis_id") + if hadis_id: + qs = qs.filter(hadis_id=hadis_id) + return qs.order_by("id") + + class AdminHadisInterpretationViewSet(ModelViewSet): serializer_class = AdminHadisInterpretationSerializer permission_classes = [IsAuthenticated, IsSuperAdmin]