Browse Source

Sync transmitters fixed n+1 and indexing

master
Mohsen Taba 5 months ago
parent
commit
00804b361a
  1. 35
      apps/hadis/migrations/0066_alter_transmitteroriginaltext_options_and_more.py
  2. 16
      apps/hadis/models/transmitter.py
  3. 63
      apps/hadis/serializers/hadis.py
  4. 18
      apps/hadis/views/transmitter.py

35
apps/hadis/migrations/0066_alter_transmitteroriginaltext_options_and_more.py

@ -0,0 +1,35 @@
# Generated by Django 4.2.27 on 2025-12-22 14:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("hadis", "0065_hadiscategory_hadis_hadis_parent__e7a217_idx_and_more"),
]
operations = [
migrations.AlterModelOptions(
name="transmitteroriginaltext",
options={
"verbose_name": "Transmitter Original Text",
"verbose_name_plural": "Transmitter Original Text",
},
),
migrations.AddIndex(
model_name="transmitteropinion",
index=models.Index(
fields=["transmitter"], name="hadis_trans_transmi_0f1df2_idx"
),
),
migrations.AddIndex(
model_name="transmitteroriginaltext",
index=models.Index(
fields=["transmitter"], name="hadis_trans_transmi_fff93f_idx"
),
),
migrations.AddIndex(
model_name="transmitters",
index=models.Index(fields=["id"], name="hadis_trans_id_bd318c_idx"),
),
]

16
apps/hadis/models/transmitter.py

@ -148,6 +148,10 @@ class Transmitters(models.Model):
updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at'))
class Meta:
indexes = [
# For ordering in sync API
models.Index(fields=['id']),
]
verbose_name = _('Transmitter')
verbose_name_plural = _('Transmitters')
ordering = ('full_name',)
@ -303,6 +307,10 @@ class TransmitterOpinion(models.Model):
updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at'))
class Meta:
indexes = [
# For filtering by transmitter + ordering
models.Index(fields=['transmitter']),
]
verbose_name = _('Transmitter Opinion')
verbose_name_plural = _('Transmitter Opinions')
ordering = ('-created_at',)
@ -357,6 +365,14 @@ class TransmitterOriginalText(models.Model):
translation = models.JSONField(verbose_name=_('translation'), default=list)
share_link = models.CharField(max_length=255, verbose_name=_('share link'), null=True, blank=True)
class Meta:
indexes = [
# For filtering by transmitter + ordering
models.Index(fields=['transmitter']),
]
verbose_name = _('Transmitter Original Text')
verbose_name_plural = _('Transmitter Original Text')
def __str__(self):
return f"{self.title[0]['text']} by {self.transmitter.full_name[0]['text']}"

63
apps/hadis/serializers/hadis.py

@ -323,16 +323,10 @@ class TransmitterDetailSerializer(serializers.ModelSerializer):
class TransmitterSyncSerializer(serializers.ModelSerializer):
"""Serializer for syncing all transmitter data for offline mode"""
# Biographical data group (flattened)
biographical = serializers.SerializerMethodField()
# Scholar's opinions group
scholars_opinions = serializers.SerializerMethodField()
# Original texts group
original_texts = serializers.SerializerMethodField()
full_name =LocalizedField()
full_name = LocalizedField()
class Meta:
model = Transmitters
@ -342,15 +336,16 @@ class TransmitterSyncSerializer(serializers.ModelSerializer):
def get_biographical(self, obj):
"""Get biographical information (flattened)"""
request = self.context.get('request')
request = self.context.get('request') # ← FIX: Define request
return {
'full_name': get_localized_text(obj.full_name,request),
'kunya': get_localized_text(obj.kunya,request),
'known_as':get_localized_text(obj.known_as,request) ,
'nickname':get_localized_text( obj.nickname,request),
'origin': get_localized_text(obj.origin,request),
'lived_in':get_localized_text(obj.lived_in,request) ,
'died_in': get_localized_text(obj.died_in,request),
'full_name': get_localized_text(obj.full_name, request),
'kunya': get_localized_text(obj.kunya, request),
'known_as': get_localized_text(obj.known_as, request),
'nickname': get_localized_text(obj.nickname, request),
'origin': get_localized_text(obj.origin, request),
'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,
@ -359,38 +354,40 @@ class TransmitterSyncSerializer(serializers.ModelSerializer):
'madhhab': obj.madhhab,
'in_sahih_muslim': obj.in_sahih_muslim,
'in_sahih_bukhari': obj.in_sahih_bukhari,
'description': get_localized_text(obj.description,request),
'description': get_localized_text(obj.description, request),
'thumbnail': obj.thumbnail.url if obj.thumbnail else None,
}
def get_scholars_opinions(self, obj):
"""Get all scholarly opinions about this transmitter"""
request = self.context.get('request')
opinions = []
for opinion in obj.opinions.all():
opinions.append({
request = self.context.get('request') # ← FIX: Define request
return [
{
'id': opinion.id,
'scholar_name': get_localized_text(opinion.scholar_name,request),
'opinion_text': get_localized_text(opinion.opinion_text,request),
'scholar_name': get_localized_text(opinion.scholar_name, request),
'opinion_text': get_localized_text(opinion.opinion_text, request),
'status': opinion.status,
'created_at': opinion.created_at.isoformat() if opinion.created_at else None,
'updated_at': opinion.updated_at.isoformat() if opinion.updated_at else None,
})
return opinions
}
for opinion in obj.opinions.all() # Already prefetched
]
def get_original_texts(self, obj):
"""Get original texts of the transmitter"""
request = self.context.get('request')
texts = []
for t in obj.originaltexts.all():
texts.append({
request = self.context.get('request') # ← FIX: Define request
return [
{
'id': t.id,
'title': get_localized_text(t.title,request),
'text': get_localized_text(t.text,request),
'translation': get_localized_text(t.translation,request),
'title': get_localized_text(t.title, request),
'text': get_localized_text(t.text, request),
'translation': get_localized_text(t.translation, request),
'share_link': t.share_link,
})
return texts
}
for t in obj.originaltexts.all() # Already prefetched
]

18
apps/hadis/views/transmitter.py

@ -91,6 +91,7 @@ class TransmitterOriginalTextView(ListAPIView):
)
class TransmitterSyncView(ListAPIView):
"""
API view to sync all transmitter data for offline mode
@ -104,15 +105,26 @@ class TransmitterSyncView(ListAPIView):
return self.list(request, *args, **kwargs)
def get_queryset(self):
return Transmitters.objects.prefetch_related('opinions', 'originaltexts').order_by('id')
"""
Prefetch ALL related data at once
"""
return (
Transmitters.objects
.prefetch_related(
'opinions', # Already prefetched
'originaltexts' # Already prefetched
)
.order_by('id')
)
def list(self, request, *args, **kwargs):
queryset = self.get_queryset()
serializer = self.get_serializer(queryset, many=True, context={'request': request})
response_data = {
'count': queryset.count(),
'count': len(serializer.data), # ← No extra query!
'results': serializer.data
}
return Response(response_data)
return Response(response_data)
Loading…
Cancel
Save