From 21682622ff53ad4832445684becfa306c184aafe Mon Sep 17 00:00:00 2001 From: mohsentaba Date: Mon, 22 Dec 2025 13:32:46 +0330 Subject: [PATCH] Sync hadis fixed n+1 and indexing --- ...064_hadis_hadis_hadis_status_5e0de5_idx.py | 18 ++++++ apps/hadis/models/hadis.py | 4 ++ apps/hadis/serializers/hadis.py | 60 +++++++++++-------- apps/hadis/views/hadis.py | 31 ++++++---- 4 files changed, 79 insertions(+), 34 deletions(-) create mode 100644 apps/hadis/migrations/0064_hadis_hadis_hadis_status_5e0de5_idx.py diff --git a/apps/hadis/migrations/0064_hadis_hadis_hadis_status_5e0de5_idx.py b/apps/hadis/migrations/0064_hadis_hadis_hadis_status_5e0de5_idx.py new file mode 100644 index 0000000..acdc62e --- /dev/null +++ b/apps/hadis/migrations/0064_hadis_hadis_hadis_status_5e0de5_idx.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.27 on 2025-12-22 13:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("hadis", "0063_alter_transmitteroriginaltext_slug"), + ] + + operations = [ + migrations.AddIndex( + model_name="hadis", + index=models.Index( + fields=["status", "id"], name="hadis_hadis_status_5e0de5_idx" + ), + ), + ] diff --git a/apps/hadis/models/hadis.py b/apps/hadis/models/hadis.py index 624627a..50755a2 100644 --- a/apps/hadis/models/hadis.py +++ b/apps/hadis/models/hadis.py @@ -293,6 +293,10 @@ class Hadis(models.Model): super().save(*args, **kwargs) class Meta: + indexes = [ + # Optimizes: Hadis.objects.filter(status=True).order_by('id') + models.Index(fields=['status', 'id']), + ] verbose_name = _('hadis') verbose_name_plural = _('hadises') ordering = ('category', 'number') diff --git a/apps/hadis/serializers/hadis.py b/apps/hadis/serializers/hadis.py index e3b8735..bd23f86 100644 --- a/apps/hadis/serializers/hadis.py +++ b/apps/hadis/serializers/hadis.py @@ -54,7 +54,6 @@ class HadisSyncSerializer(serializers.ModelSerializer): ] def get_detail(self, obj): - """Detail group: address, status, share, links, tags, references, reference_images""" request = self.context.get('request') # status @@ -62,24 +61,33 @@ class HadisSyncSerializer(serializers.ModelSerializer): if obj.hadis_status: status_block = { 'id': obj.hadis_status.id, - 'title': get_localized_text(obj.hadis_status.title,request), - 'color': obj.hadis_status.color + 'title': get_localized_text(obj.hadis_status.title, request), + 'color': obj.hadis_status.color, } - # tags - tags_block = [{'id': tag.id, 'title': get_localized_text(tag.title,request)} for tag in obj.tags.all()] + # 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 + # references and reference images (already prefetched) references_block = [] reference_images_block = [] - for reference in obj.references.select_related('book_reference').prefetch_related('book_reference__authors', 'images'): + + for reference in obj.references.all(): book = reference.book_reference + authors = book.authors.all() if book 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 book.authors.all()] if book else [], + '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': book.description if book else None, }) + for img in reference.images.all().order_by('priority'): thumb_url = None if img.thumbnail: @@ -91,9 +99,9 @@ class HadisSyncSerializer(serializers.ModelSerializer): }) return { - 'address': get_localized_text(obj.address,request), + 'address': get_localized_text(obj.address, request), 'hadis_status': status_block, - 'status_text':get_localized_text(obj.hadis_status_text,request) , + 'status_text': get_localized_text(obj.hadis_status_text, request), 'share_link': obj.share_link, 'links': obj.links, 'tags': tags_block, @@ -101,17 +109,20 @@ class HadisSyncSerializer(serializers.ModelSerializer): 'reference_images': reference_images_block, } + def get_narrators(self, obj): - """Narrators group: description + transmitters""" + request = self.context.get('request') + transmitters_data = [] - for transmitter_rel in obj.transmitters.select_related('transmitter', 'narrator_layer').order_by('order'): + for transmitter_rel in obj.transmitters.all(): t = transmitter_rel.transmitter + layer = transmitter_rel.narrator_layer transmitters_data.append({ 'id': t.id, - 'name': get_localized_text(t.full_name,request), + 'name': get_localized_text(t.full_name, request), 'reliability': t.reliability, - 'layer_level': transmitter_rel.narrator_layer.number if transmitter_rel.narrator_layer else None, - 'layer_name': get_localized_text(transmitter_rel.narrator_layer.name,request) if transmitter_rel.narrator_layer else None, + 'layer_level': layer.number if layer else None, + 'layer_name': get_localized_text(layer.name, request) if layer else None, 'is_gap': transmitter_rel.is_gap, 'birth_year_hijri': t.birth_year_hijri, 'death_year_hijri': t.death_year_hijri, @@ -119,23 +130,24 @@ class HadisSyncSerializer(serializers.ModelSerializer): }) return { - 'description': get_localized_text(obj.description,request), - 'transmitters': transmitters_data + 'description': get_localized_text(obj.description, request), + 'transmitters': transmitters_data, } + def get_explanations(self, obj): - """Explanations group""" - return get_localized_text(obj.explanation,request) + request = self.context.get('request') + return get_localized_text(obj.explanation, request) def get_corrections(self, obj): - """Corrections group""" + request = self.context.get('request') corrections_data = [] for correction in obj.hadiscorrection_set.all(): corrections_data.append({ 'id': correction.id, - 'title': get_localized_text(correction.title,request), - 'description': get_localized_text(correction.description,request), - 'translation': get_localized_text(correction.translation,request), + 'title': get_localized_text(correction.title, request), + 'description': get_localized_text(correction.description, request), + 'translation': get_localized_text(correction.translation, request), 'share_link': correction.share_link, }) return corrections_data diff --git a/apps/hadis/views/hadis.py b/apps/hadis/views/hadis.py index e9b3216..e918161 100644 --- a/apps/hadis/views/hadis.py +++ b/apps/hadis/views/hadis.py @@ -6,7 +6,7 @@ from django.db.models import Count from django.db.models import Prefetch from ..serializers.category import get_localized_text -from ..models import HadisCategory, Hadis, HadisCollection,HadisTransmitter , HadisCorrection +from ..models import HadisCategory, Hadis, HadisCollection,HadisTransmitter , HadisCorrection ,HadisReference from ..serializers import HadisListSerializer, HadisBasicSerializer, HadisDetailSerializer, HadisCollectionListSerializer, HadisSyncSerializer,HadisCorrectionSerializer,HadisTransmitterListSerializer from ..docs import hadis_list_swagger, hadis_detail_swagger, hadis_collections_swagger, hadis_sync_swagger, hadis_transmitters_swagger, hadis_corrections_swagger, hadis_basic_swagger, hadis_main_list_swagger @@ -32,15 +32,26 @@ class HadisSyncView(ListAPIView): pagination_class = NoPagination def get_queryset(self): - return Hadis.objects.filter(status=True).select_related( - 'category', 'hadis_status' - ).prefetch_related( - 'tags', - 'transmitters__transmitter', - 'references__images', - 'references__book_reference', - 'hadiscorrection_set' - ).order_by('id') + return ( + Hadis.objects + .filter(status=True) + .select_related('category', 'hadis_status') + .prefetch_related( + 'tags', + Prefetch( + 'transmitters', + queryset=HadisTransmitter.objects.select_related('transmitter', 'narrator_layer').order_by('order') + ), + Prefetch( + 'references', + queryset=HadisReference.objects + .select_related('book_reference') + .prefetch_related('book_reference__authors', 'images') + ), + 'hadiscorrection_set', + ) + .order_by('id') + ) @hadis_sync_swagger def get(self, request, *args, **kwargs):