From bf07701b24397cc889c96e5561a8b965b9a62235 Mon Sep 17 00:00:00 2001 From: mohsentaba Date: Sat, 29 Aug 2026 15:11:09 +0330 Subject: [PATCH] feat: naraytor layer logic updated to connect to hadith directly --- ...er_options_narratorlayer_hadis_and_more.py | 33 +++++++++++++++++++ apps/hadis/models/transmitter.py | 20 +++++++---- apps/hadis/serializers/hadis.py | 20 +++-------- apps/hadis/serializers/serializers_admin.py | 31 +++++++++++++++-- apps/hadis/views/hadis.py | 12 ++----- apps/hadis/views_admin.py | 9 +++-- 6 files changed, 88 insertions(+), 37 deletions(-) create mode 100644 apps/hadis/migrations/0036_alter_narratorlayer_options_narratorlayer_hadis_and_more.py diff --git a/apps/hadis/migrations/0036_alter_narratorlayer_options_narratorlayer_hadis_and_more.py b/apps/hadis/migrations/0036_alter_narratorlayer_options_narratorlayer_hadis_and_more.py new file mode 100644 index 0000000..29871fb --- /dev/null +++ b/apps/hadis/migrations/0036_alter_narratorlayer_options_narratorlayer_hadis_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 4.2.30 on 2026-08-29 13:51 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('hadis', '0035_bookreference_has_editions'), + ] + + operations = [ + migrations.AlterModelOptions( + name='narratorlayer', + options={'ordering': ['hadis', 'number', 'id'], 'verbose_name': 'Narrator Layer', 'verbose_name_plural': 'Narrator Layers'}, + ), + migrations.AddField( + model_name='narratorlayer', + name='hadis', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='narrator_layers', to='hadis.hadis', verbose_name='hadis'), + ), + migrations.AlterField( + model_name='narratorlayer', + name='number', + field=models.PositiveIntegerField(verbose_name='layer number'), + ), + migrations.AlterField( + model_name='narratorlayer', + name='slug', + field=models.SlugField(allow_unicode=True, blank=True, max_length=255, null=True, verbose_name='slug'), + ), + ] diff --git a/apps/hadis/models/transmitter.py b/apps/hadis/models/transmitter.py index cf8b06a..bc0a1d8 100644 --- a/apps/hadis/models/transmitter.py +++ b/apps/hadis/models/transmitter.py @@ -15,12 +15,20 @@ class NarratorLayer(LowercaseSlugMixin, models.Model): slug_source_field = 'name' """ Model for narrator layers/classes (Tabaqat) - Represents the classification level of narrators in hadis chains + Represents the classification level of narrators in a specific hadis chain """ - name = models.JSONField(default = list , verbose_name=_('Name')) - number = models.PositiveIntegerField(verbose_name=_('layer number'), unique=True) - description = models.JSONField(default = list , verbose_name=_('Description')) - slug = models.SlugField(max_length=255,unique=True, verbose_name=_('slug'), blank=True, allow_unicode=True) + hadis = models.ForeignKey( + "hadis.Hadis", + on_delete=models.CASCADE, + related_name="narrator_layers", + verbose_name=_("hadis"), + null=True, + blank=True, + ) + name = models.JSONField(default=list, verbose_name=_('Name')) + number = models.PositiveIntegerField(verbose_name=_('layer number')) + description = models.JSONField(default=list, verbose_name=_('Description')) + slug = models.SlugField(max_length=255, verbose_name=_('slug'), blank=True, null=True, allow_unicode=True) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at')) updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at')) @@ -28,7 +36,7 @@ class NarratorLayer(LowercaseSlugMixin, models.Model): class Meta: verbose_name = _('Narrator Layer') verbose_name_plural = _('Narrator Layers') - ordering = ['number'] + ordering = ['hadis', 'number', 'id'] def __str__(self): name = self.name[0]['text'] if self.name else f"Layer {self.number}" diff --git a/apps/hadis/serializers/hadis.py b/apps/hadis/serializers/hadis.py index bea9834..e92b412 100644 --- a/apps/hadis/serializers/hadis.py +++ b/apps/hadis/serializers/hadis.py @@ -730,30 +730,20 @@ class HadisTransmitterListSerializer(serializers.ModelSerializer): fields = ['id', 'layer_count','layer_names', 'results'] def get_layer_count(self, obj): - # Calculate distinct layers efficiently - return obj.transmitters.values('narrator_layer').distinct().count() + return obj.narrator_layers.count() + def get_layer_names(self, obj): """Get list of localized narrator layer names""" request = self.context.get('request') + layer_objects = obj.narrator_layers.all().order_by('number') - # Import here to get actual objects - from apps.hadis.models import NarratorLayer - - # Get ALL distinct narrator layer IDs for this hadis (not filtered) - # This ensures layer names are returned regardless of layer filtering - all_layers_for_hadis = HadisTransmitter.objects.filter( - hadis=obj - ).values_list('narrator_layer', flat=True).distinct() - - layer_objects = NarratorLayer.objects.filter(id__in=all_layers_for_hadis) - - # Extract localized names layer_names = [] for layer in layer_objects: - name =get_localized_text(layer.name, request=request) + name = get_localized_text(layer.name, request=request) description = get_localized_text(layer.description, request=request) slug = layer.slug layer_names.append({ + 'id': layer.id, 'name': name, 'slug': slug, 'description': description diff --git a/apps/hadis/serializers/serializers_admin.py b/apps/hadis/serializers/serializers_admin.py index 6bb73b9..a65c325 100644 --- a/apps/hadis/serializers/serializers_admin.py +++ b/apps/hadis/serializers/serializers_admin.py @@ -913,12 +913,37 @@ class AdminTransmitterReliabilitySerializer(serializers.ModelSerializer): class AdminNarratorLayerSerializer(serializers.ModelSerializer): name = serializers.JSONField() - description = serializers.JSONField() + description = serializers.JSONField(required=False, default=list) + hadis = serializers.PrimaryKeyRelatedField( + queryset=Hadis.objects.all(), + required=True, + ) + hadis_detail = serializers.SerializerMethodField() class Meta: model = NarratorLayer - fields = ["id", "name", "number", "description", "slug", "created_at", "updated_at"] - read_only_fields = ["id", "created_at", "updated_at"] + fields = [ + "id", + "hadis", + "hadis_detail", + "name", + "number", + "description", + "slug", + "created_at", + "updated_at", + ] + read_only_fields = ["id", "hadis_detail", "created_at", "updated_at"] + + def get_hadis_detail(self, obj): + if not obj.hadis: + return None + return { + "id": obj.hadis.id, + "number": obj.hadis.number, + "slug": obj.hadis.slug, + "title": obj.hadis.title, + } class AdminOpinionStatusSerializer(serializers.ModelSerializer): diff --git a/apps/hadis/views/hadis.py b/apps/hadis/views/hadis.py index 664e77b..40a8cf0 100644 --- a/apps/hadis/views/hadis.py +++ b/apps/hadis/views/hadis.py @@ -667,16 +667,8 @@ class HadisLayersView(ListAPIView): # Get the hadis object to ensure it exists hadis = get_object_or_404(Hadis, slug=hadis_slug, status=True) - # Get all distinct narrator layer IDs for this hadis - layer_ids = HadisTransmitter.objects.filter( - hadis=hadis - ).values_list('narrator_layer', flat=True).distinct() - - # Filter out None values (transmitters without layers) - layer_ids = [lid for lid in layer_ids if lid is not None] - - # Return the layer objects ordered by number - return NarratorLayer.objects.filter(id__in=layer_ids).order_by('number') + # Return the layer objects for this hadis ordered by number + return NarratorLayer.objects.filter(hadis=hadis).order_by('number') class HadisFiltersView(ListAPIView): diff --git a/apps/hadis/views_admin.py b/apps/hadis/views_admin.py index f3301bb..58b31be 100644 --- a/apps/hadis/views_admin.py +++ b/apps/hadis/views_admin.py @@ -216,7 +216,10 @@ class DovodiNarratorLayerViewSet(ModelViewSet): parser_classes = (JSONParser, MultiPartParser, FormParser) def get_queryset(self): - queryset = NarratorLayer.objects.all() + queryset = NarratorLayer.objects.select_related("hadis").all() + hadis_id = self.request.query_params.get("hadis") or self.request.query_params.get("hadis_id") + if hadis_id: + queryset = queryset.filter(hadis_id=hadis_id) search_query = self.request.query_params.get("search") if search_query: queryset = queryset.filter( @@ -392,7 +395,7 @@ class AdminHadisCorrectionViewSet(ModelViewSet): queryset = queryset.filter( Q(title__icontains=search) | Q(text__icontains=search) ) - return queryset.order_by("priority", "created_at") + return queryset.order_by("-created_at", "-id") def perform_create(self, serializer): priority = self.request.data.get("priority", 1) @@ -436,7 +439,7 @@ class AdminHadisInterpretationViewSet(ModelViewSet): queryset = queryset.filter( Q(title__icontains=search) | Q(text__icontains=search) ) - return queryset.order_by("priority", "created_at") + return queryset.order_by("-created_at", "-id") def perform_create(self, serializer): priority = self.request.data.get("priority", 1)