Browse Source

update swagger and debug reference serializers

master
Mohsen Taba 5 months ago
parent
commit
d587139456
  1. 912
      apps/hadis/docs.py
  2. 30
      apps/hadis/migrations/0038_narratorlayer_slug_alter_referenceimage_reference.py
  3. 19
      apps/hadis/migrations/0039_alter_narratorlayer_slug.py
  4. 19
      apps/hadis/migrations/0040_bookreference_slug.py
  5. 16
      apps/hadis/migrations/0041_remove_bookreference_slug.py
  6. 19
      apps/hadis/migrations/0042_bookreference_slug.py
  7. 17
      apps/hadis/migrations/0043_bookreference_publisher.py
  8. 16
      apps/hadis/migrations/0044_remove_bookreference_publisher.py
  9. 17
      apps/hadis/migrations/0045_bookreference_publisher.py
  10. 13
      apps/hadis/models/reference.py
  11. 8
      apps/hadis/models/transmitter.py
  12. 53
      apps/hadis/serializers/hadis.py
  13. 64
      apps/hadis/serializers/reference.py
  14. 70
      apps/hadis/views/hadis.py
  15. 43
      apps/hadis/views/reference.py
  16. BIN
      seeds/images/book1.png
  17. BIN
      seeds/images/book2.png
  18. BIN
      seeds/images/book3.png
  19. BIN
      seeds/images/book4.png
  20. BIN
      seeds/images/book5.png

912
apps/hadis/docs.py
File diff suppressed because it is too large
View File

30
apps/hadis/migrations/0038_narratorlayer_slug_alter_referenceimage_reference.py

@ -0,0 +1,30 @@
# Generated by Django 5.2.9 on 2025-12-16 10:14
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("hadis", "0037_remove_bookattribute_book_references_and_more"),
]
operations = [
migrations.AddField(
model_name="narratorlayer",
name="slug",
field=models.SlugField(
blank=True, max_length=255, null=True, verbose_name="slug"
),
),
migrations.AlterField(
model_name="referenceimage",
name="reference",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="images",
to="hadis.hadisreference",
verbose_name="Hadis Reference",
),
),
]

19
apps/hadis/migrations/0039_alter_narratorlayer_slug.py

@ -0,0 +1,19 @@
# Generated by Django 5.2.9 on 2025-12-16 10:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("hadis", "0038_narratorlayer_slug_alter_referenceimage_reference"),
]
operations = [
migrations.AlterField(
model_name="narratorlayer",
name="slug",
field=models.SlugField(
blank=True, max_length=255, unique=True, verbose_name="slug"
),
),
]

19
apps/hadis/migrations/0040_bookreference_slug.py

@ -0,0 +1,19 @@
# Generated by Django 5.2.9 on 2025-12-16 12:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("hadis", "0039_alter_narratorlayer_slug"),
]
operations = [
migrations.AddField(
model_name="bookreference",
name="slug",
field=models.SlugField(
blank=True, max_length=255, null=True, verbose_name="slug"
),
),
]

16
apps/hadis/migrations/0041_remove_bookreference_slug.py

@ -0,0 +1,16 @@
# Generated by Django 5.2.9 on 2025-12-16 12:25
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("hadis", "0040_bookreference_slug"),
]
operations = [
migrations.RemoveField(
model_name="bookreference",
name="slug",
),
]

19
apps/hadis/migrations/0042_bookreference_slug.py

@ -0,0 +1,19 @@
# Generated by Django 5.2.9 on 2025-12-16 12:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("hadis", "0041_remove_bookreference_slug"),
]
operations = [
migrations.AddField(
model_name="bookreference",
name="slug",
field=models.SlugField(
blank=True, max_length=255, unique=True, verbose_name="slug"
),
),
]

17
apps/hadis/migrations/0043_bookreference_publisher.py

@ -0,0 +1,17 @@
# Generated by Django 5.2.9 on 2025-12-16 12:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("hadis", "0042_bookreference_slug"),
]
operations = [
migrations.AddField(
model_name="bookreference",
name="publisher",
field=models.TextField(blank=True, null=True, verbose_name="publisher"),
),
]

16
apps/hadis/migrations/0044_remove_bookreference_publisher.py

@ -0,0 +1,16 @@
# Generated by Django 5.2.9 on 2025-12-16 12:54
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("hadis", "0043_bookreference_publisher"),
]
operations = [
migrations.RemoveField(
model_name="bookreference",
name="publisher",
),
]

17
apps/hadis/migrations/0045_bookreference_publisher.py

@ -0,0 +1,17 @@
# Generated by Django 5.2.9 on 2025-12-16 12:55
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("hadis", "0044_remove_bookreference_publisher"),
]
operations = [
migrations.AddField(
model_name="bookreference",
name="publisher",
field=models.TextField(blank=True, null=True, verbose_name="publisher"),
),
]

13
apps/hadis/models/reference.py

@ -15,7 +15,8 @@ class BookReference(models.Model):
volume = models.CharField(max_length=100, verbose_name=_('volume'), blank=True, null=True)
year_of_publication = models.CharField(max_length=50, verbose_name=_('year of publication'), blank=True, null=True)
number_page = models.PositiveIntegerField(verbose_name=_('number of pages'), blank=True, null=True)
# publisher = models.TextField(verbose_name=_('publisher'),blank=True,null=True)
slug = models.SlugField(max_length=255, verbose_name=_('slug'), blank=True,unique=True)
publisher = models.TextField(verbose_name=_('publisher'),blank=True,null=True)
rate = models.DecimalField(
max_digits=3,
decimal_places=2,
@ -36,6 +37,16 @@ class BookReference(models.Model):
def __str__(self):
return self.title
def save(self, *args, **kwargs):
if not self.slug:
base_slug = slugify(self.title, allow_unicode=True)
slug = base_slug
counter = 1
while BookReference.objects.filter(slug=slug).exclude(pk=self.pk).exists():
slug = f"{base_slug}-{counter}"
counter += 1
self.slug = slug
super().save(*args, **kwargs)
class BookReferenceImage(models.Model):
"""

8
apps/hadis/models/transmitter.py

@ -3,6 +3,7 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from filer.fields.image import FilerImageField
from django.utils.text import slugify
@ -14,6 +15,7 @@ class NarratorLayer(models.Model):
name = models.CharField(max_length=255, verbose_name=_('name'))
number = models.PositiveIntegerField(verbose_name=_('layer number'), unique=True)
description = models.TextField(verbose_name=_('description'), blank=True, null=True)
slug = models.SlugField(max_length=255,unique=True, verbose_name=_('slug'), blank=True)
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at'))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at'))
@ -26,6 +28,12 @@ class NarratorLayer(models.Model):
def __str__(self):
return f"{_('Layer')} {self.number} - {self.name}"
def save(self, *args, **kwargs):
if not self.slug:
slug = slugify(self.name)
self.slug = slug
super().save(*args, **kwargs)
class Transmitters(models.Model):
class ReliabilityLevel(models.TextChoices):

53
apps/hadis/serializers/hadis.py

@ -144,7 +144,6 @@ class HadisSyncSerializer(serializers.ModelSerializer):
})
return corrections_data
class HadisListSerializer(serializers.ModelSerializer):
"""Serializer for Hadis list"""
category = serializers.SerializerMethodField()
@ -199,6 +198,16 @@ class TransmitterSerializer(serializers.ModelSerializer):
'id', 'full_name', 'birth_year_hijri', 'death_year_hijri',
"known_as",'nickname','reliability','madhhab','generation'
]
class TransmitterShortSerializer(serializers.ModelSerializer):
"""Serializer for Transmitters"""
class Meta:
model = Transmitters
fields = [
'id', 'full_name', 'birth_year_hijri', 'death_year_hijri',
"known_as",'nickname','reliability'
]
class TransmitterOpinionSerializer(serializers.ModelSerializer):
""" Serializer for TransmitterOpinions """
@ -213,12 +222,6 @@ class TransmitterOriginalTextSerializer(serializers.ModelSerializer):
model = TransmitterOriginalText
fields = ['id', 'title', 'text', 'translation', 'share_link']
class HadisTransmitterSerializer(serializers.ModelSerializer):
""" Serializer for HadisTransmitters """
class Meta:
model = HadisTransmitter
fields = '__all__'
class TransmitterDetailSerializer(serializers.ModelSerializer):
""" Serializer for Details of Transmitters """
@ -304,14 +307,44 @@ class TransmitterSyncSerializer(serializers.ModelSerializer):
class HadisTransmitterSerializer(serializers.ModelSerializer):
"""Serializer for HadisTransmitter with transmitter details"""
transmitter = TransmitterSerializer(read_only=True)
transmitter = TransmitterShortSerializer(read_only=True)
narrator_layer_description = serializers.SerializerMethodField()
layer = serializers.SerializerMethodField()
class Meta:
model = HadisTransmitter
fields = [
'id', 'transmitter', 'order', 'is_gap','narrator_layer'
'id', 'order', 'is_gap','narrator_layer_description','layer', 'transmitter'
]
def get_narrator_layer_description(self, obj):
"""Get narrator layer description"""
return obj.narrator_layer.description
def get_layer(self, obj):
"""Get narrator layer slug"""
return obj.narrator_layer.slug
# serializers.py
class HadisTransmitterListSerializer(serializers.ModelSerializer):
"""
The 'Parent' Serializer.
It takes a HADIS object and returns the count + the list of transmitters.
"""
layer_count = serializers.SerializerMethodField()
results = HadisTransmitterSerializer(
source='transmitters', # Access the 'transmitters' reverse relation
many=True,
read_only=True
)
class Meta:
model = Hadis
fields = ['id', 'layer_count', 'results']
def get_layer_count(self, obj):
# Calculate distinct layers efficiently
return obj.transmitters.values('narrator_layer').distinct().count()
class ReferenceImageSerializer(serializers.ModelSerializer):
"""Serializer for ReferenceImage"""

64
apps/hadis/serializers/reference.py

@ -6,12 +6,12 @@ from ..models import BookReference , BookAuthor , BookReferenceImage, HadisRefer
class BookAuthorSerializer(serializers.ModelSerializer):
class Meta:
model = BookAuthor
fields = '__all__'
fields = ['id','name']
class BookReferenceImageSerializer(serializers.ModelSerializer):
class Meta:
model = BookReferenceImage
fields = '__all__'
fields = ['id','image','description','order']
class BookReferenceSerializer(serializers.ModelSerializer):
image = BookReferenceImageSerializer(
@ -24,63 +24,80 @@ class BookReferenceSerializer(serializers.ModelSerializer):
model = BookReference
fields = ['id','title','rate','author','description','image','volume']
def get_author (self,obj):
author = obj.bookauthor_set
return author.name
author = obj.authors.all()
return [
{
'id': author.id,
'name': author.name
}
for author in author
]
class BookAttributeSerializer(serializers.ModelSerializer):
class Meta:
model = BookAttribute
fields = ['id', 'title', 'value', 'book_reference', 'created_at', 'updated_at']
read_only_fields = ['id', 'created_at', 'updated_at']
fields = ['id', 'title', 'value','book_reference']
class BookDetailSerializer(serializers.ModelSerializer):
attribute = BookAttributeSerializer(
many=True,
read_only = True,
source = 'bookattribute_set'
source = 'attributes'
)
author = BookAuthorSerializer(
read_only = True ,
source = 'bookauthor_set'
author= BookAuthorSerializer(
many=True,
read_only=True,
source='authors'
)
image = BookReferenceImageSerializer(
many= True ,
read_only = True ,
source = 'bookreference_set'
many=True,
read_only=True,
source='images'
)
hadis = HadisListSerializer(
many=True,
read_only=True,
source='hadisreference_set'
source='hadis_references'
)
class Meta:
model = BookReference
fields = '__all__'
# def create(self , validated_data):
# author = validated_data.pop('author')
# book = BookReference.objects.create(**validated_data)
# for author in author
fields = ['id','title','rate','isbn','language','number_page','publisher','description','volume','slug','attribute','author','image','hadis']
class BookReferenceSyncSerializer(serializers.ModelSerializer):
"""Serializer for syncing all book reference data for offline mode"""
attribute = BookAttributeSerializer(
many=True,
read_only = True,
source = 'attributes'
)
author= BookAuthorSerializer(
many=True,
read_only=True,
source='authors'
)
image = BookReferenceImageSerializer(
many=True,
read_only=True,
source='images'
)
# Basic information
detail = serializers.SerializerMethodField()
# Hadis group (related hadises)
hadises = serializers.SerializerMethodField()
authors = serializers.SerializerMethodField()
# authors = serializers.SerializerMethodField()
class Meta:
model = BookReference
fields = [
'id', 'title','rate' , 'authors' ,'detail', 'hadises'
'id', 'title','rate' , 'author' ,'detail','image','attribute', 'hadises'
]
def get_authors(self,obj):
authors = []
@ -102,6 +119,7 @@ class BookReferenceSyncSerializer(serializers.ModelSerializer):
'volume': obj.volume,
'language': obj.language,
'isbn': obj.isbn,
'number_page':obj.number_page,
'year_of_publication': obj.year_of_publication,
'number_of_pages': obj.number_page,
'volume_info': obj.volume,

70
apps/hadis/views/hadis.py

@ -2,9 +2,11 @@ from rest_framework.generics import ListAPIView, RetrieveAPIView
from django.shortcuts import get_object_or_404
from utils.pagination import NoPagination
from rest_framework.response import Response
from django.db.models import Count
from django.db.models import Prefetch
from ..models import HadisCategory, Hadis, HadisCollection
from ..serializers import HadisListSerializer, HadisBasicSerializer, HadisDetailSerializer, HadisCollectionListSerializer, HadisSyncSerializer
from ..models import HadisCategory, Hadis, HadisCollection,HadisTransmitter
from ..serializers import HadisListSerializer, HadisBasicSerializer, HadisDetailSerializer, HadisCollectionListSerializer, HadisSyncSerializer,HadisTransmitterSerializer,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
@ -73,7 +75,12 @@ class HadisListView(ListAPIView):
return Hadis.objects.filter(
category_id=category_id,
status=True
).order_by('number')
).order_by('number').annotate(
# distinct=True is CRITICAL here.
# Without it, if 3 narrators are from "Layer 1", it counts as 3.
# With it, it counts as 1 (unique layer).
layer_count=Count('transmitters__narrator_layer', distinct=True)
)
class HadisBasicView(RetrieveAPIView):
@ -116,48 +123,39 @@ class HadisDetailView(RetrieveAPIView):
'references__book_reference__description',
)
class HadisTransmittersView(RetrieveAPIView):
"""
API view to retrieve transmitters for a specific hadis
Fetches a single Hadis but filters the nested Transmitters list
if a ?layer=slug param is provided.
"""
serializer_class = HadisDetailSerializer
lookup_field = 'id'
serializer_class = HadisTransmitterListSerializer
lookup_url_kwarg = 'hadis_id'
@hadis_transmitters_swagger
def get(self, request, *args, **kwargs):
hadis = self.get_object()
transmitters_data = []
for transmitter_rel in hadis.transmitters.all().order_by('order'):
transmitter_info = {
'id': transmitter_rel.id,
'order': transmitter_rel.order,
'is_gap': transmitter_rel.is_gap,
'narrator_layer': transmitter_rel.narrator_layer,
'transmitter': {
'id': transmitter_rel.transmitter.id,
'full_name': transmitter_rel.transmitter.full_name,
'birth_year_hijri': transmitter_rel.transmitter.birth_year_hijri,
'death_year_hijri': transmitter_rel.transmitter.death_year_hijri,
'madhhab': transmitter_rel.transmitter.madhhab,
'description': transmitter_rel.transmitter.description,
'reliability': transmitter_rel.transmitter.reliability
}
}
transmitters_data.append(transmitter_info)
return Response({
'hadis_id': hadis.id,
'hadis_description': getattr(hadis, 'description', None),
'transmitters_count': len(transmitters_data),
'transmitters': transmitters_data
})
return self.retrieve(request, *args, **kwargs)
def get_queryset(self):
return Hadis.objects.filter(status=True).prefetch_related('transmitters__transmitter')
# 1. Get the filter param
layer_slug = self.request.query_params.get('layer')
# 2. Build the query for the "Child" (Transmitters)
# We start with the base optimization (select_related)
transmitter_qs = HadisTransmitter.objects.select_related(
'transmitter',
'narrator_layer'
).order_by('order')
# 3. Apply the filter to the Child Query (if param exists)
if layer_slug:
# Assumes 'NarratorLayer' has a 'slug' field.
# If not, use 'narrator_layer__name' or 'narrator_layer__id'.
transmitter_qs = transmitter_qs.filter(narrator_layer__slug=layer_slug)
# 4. Use the Prefetch object to inject this filtered list into the Parent
return Hadis.objects.filter(status=True).prefetch_related(
Prefetch('transmitters', queryset=transmitter_qs)
)
class HadisCorrectionsView(RetrieveAPIView):
"""

43
apps/hadis/views/reference.py

@ -35,8 +35,8 @@ class BookDetailView(RetrieveAPIView):
def get_queryset(self):
return BookReference.objects.filter(id = self.kwargs['bookreference_id']).prefetch_related(
'bookauthor_set__name',
'bookreferenceimage_set__image',
'authors__name',
'images__image',
)
@ -58,22 +58,22 @@ class BookReferenceSyncView(ListAPIView):
'hadis_references__hadis'
).order_by('id')
def list(self, request, *args, **kwargs):
queryset = self.get_queryset()
serializer = self.get_serializer(queryset, many=True, context={'request': request})
# def list(self, request, *args, **kwargs):
# queryset = self.get_queryset()
# serializer = self.get_serializer(queryset, many=True, context={'request': request})
# Group book references by ID for easy lookup
grouped_data = {}
for book_data in serializer.data:
book_id = str(book_data['id'])
grouped_data[book_id] = book_data
# # Group book references by ID for easy lookup
# grouped_data = {}
# for book_data in serializer.data:
# book_id = str(book_data['id'])
# grouped_data[book_id] = book_data
response_data = {
'count': queryset.count(),
'results': grouped_data
}
# response_data = {
# 'count': queryset.count(),
# 'results': grouped_data
# }
return Response(response_data)
# return Response(response_data)
@ -84,16 +84,3 @@ class BookAttributeView(ListCreateAPIView):
queryset = BookAttribute.objects.all()
serializer_class = BookAttributeSerializer
# def get_queryset(self):
# """
# Optionally filter by book_reference_id if provided as query parameter
# """
# queryset = BookAttribute.objects.all()
# book_reference_id = self.request.query_params.get('book_reference_id', None)
# if book_reference_id:
# queryset = queryset.filter(book_references__id=book_reference_id).distinct()
# return queryset.order_by('title')
# class BookReferencesView(ListAPIView):
# pass

BIN
seeds/images/book1.png

After

Width: 126  |  Height: 186  |  Size: 51 KiB

BIN
seeds/images/book2.png

After

Width: 126  |  Height: 186  |  Size: 55 KiB

BIN
seeds/images/book3.png

After

Width: 126  |  Height: 186  |  Size: 56 KiB

BIN
seeds/images/book4.png

After

Width: 126  |  Height: 186  |  Size: 57 KiB

BIN
seeds/images/book5.png

After

Width: 126  |  Height: 186  |  Size: 51 KiB

Loading…
Cancel
Save