Browse Source

update swagger and debug reference serializers

master
Mohsen Taba 5 months ago
parent
commit
d587139456
  1. 1064
      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. 68
      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

1064
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) 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) 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) 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( rate = models.DecimalField(
max_digits=3, max_digits=3,
decimal_places=2, decimal_places=2,
@ -36,6 +37,16 @@ class BookReference(models.Model):
def __str__(self): def __str__(self):
return self.title 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): class BookReferenceImage(models.Model):
""" """

8
apps/hadis/models/transmitter.py

@ -3,6 +3,7 @@
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from filer.fields.image import FilerImageField 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')) name = models.CharField(max_length=255, verbose_name=_('name'))
number = models.PositiveIntegerField(verbose_name=_('layer number'), unique=True) number = models.PositiveIntegerField(verbose_name=_('layer number'), unique=True)
description = models.TextField(verbose_name=_('description'), blank=True, null=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')) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at'))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at')) updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at'))
@ -25,6 +27,12 @@ class NarratorLayer(models.Model):
def __str__(self): def __str__(self):
return f"{_('Layer')} {self.number} - {self.name}" 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 Transmitters(models.Model):

53
apps/hadis/serializers/hadis.py

@ -144,7 +144,6 @@ class HadisSyncSerializer(serializers.ModelSerializer):
}) })
return corrections_data return corrections_data
class HadisListSerializer(serializers.ModelSerializer): class HadisListSerializer(serializers.ModelSerializer):
"""Serializer for Hadis list""" """Serializer for Hadis list"""
category = serializers.SerializerMethodField() category = serializers.SerializerMethodField()
@ -199,6 +198,16 @@ class TransmitterSerializer(serializers.ModelSerializer):
'id', 'full_name', 'birth_year_hijri', 'death_year_hijri', 'id', 'full_name', 'birth_year_hijri', 'death_year_hijri',
"known_as",'nickname','reliability','madhhab','generation' "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): class TransmitterOpinionSerializer(serializers.ModelSerializer):
""" Serializer for TransmitterOpinions """ """ Serializer for TransmitterOpinions """
@ -213,12 +222,6 @@ class TransmitterOriginalTextSerializer(serializers.ModelSerializer):
model = TransmitterOriginalText model = TransmitterOriginalText
fields = ['id', 'title', 'text', 'translation', 'share_link'] fields = ['id', 'title', 'text', 'translation', 'share_link']
class HadisTransmitterSerializer(serializers.ModelSerializer):
""" Serializer for HadisTransmitters """
class Meta:
model = HadisTransmitter
fields = '__all__'
class TransmitterDetailSerializer(serializers.ModelSerializer): class TransmitterDetailSerializer(serializers.ModelSerializer):
""" Serializer for Details of Transmitters """ """ Serializer for Details of Transmitters """
@ -304,14 +307,44 @@ class TransmitterSyncSerializer(serializers.ModelSerializer):
class HadisTransmitterSerializer(serializers.ModelSerializer): class HadisTransmitterSerializer(serializers.ModelSerializer):
"""Serializer for HadisTransmitter with transmitter details""" """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: class Meta:
model = HadisTransmitter model = HadisTransmitter
fields = [ 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): class ReferenceImageSerializer(serializers.ModelSerializer):
"""Serializer for ReferenceImage""" """Serializer for ReferenceImage"""

68
apps/hadis/serializers/reference.py

@ -6,12 +6,12 @@ from ..models import BookReference , BookAuthor , BookReferenceImage, HadisRefer
class BookAuthorSerializer(serializers.ModelSerializer): class BookAuthorSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = BookAuthor model = BookAuthor
fields = '__all__'
fields = ['id','name']
class BookReferenceImageSerializer(serializers.ModelSerializer): class BookReferenceImageSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = BookReferenceImage model = BookReferenceImage
fields = '__all__'
fields = ['id','image','description','order']
class BookReferenceSerializer(serializers.ModelSerializer): class BookReferenceSerializer(serializers.ModelSerializer):
image = BookReferenceImageSerializer( image = BookReferenceImageSerializer(
@ -24,63 +24,80 @@ class BookReferenceSerializer(serializers.ModelSerializer):
model = BookReference model = BookReference
fields = ['id','title','rate','author','description','image','volume'] fields = ['id','title','rate','author','description','image','volume']
def get_author (self,obj): 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 BookAttributeSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = BookAttribute 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): class BookDetailSerializer(serializers.ModelSerializer):
attribute = BookAttributeSerializer( attribute = BookAttributeSerializer(
many=True,
read_only = True, read_only = True,
source = 'bookattribute_set'
source = 'attributes'
)
author= BookAuthorSerializer(
many=True,
read_only=True,
source='authors'
) )
author = BookAuthorSerializer(
read_only = True ,
source = 'bookauthor_set'
)
image = BookReferenceImageSerializer( image = BookReferenceImageSerializer(
many= True ,
read_only = True ,
source = 'bookreference_set'
)
many=True,
read_only=True,
source='images'
)
hadis = HadisListSerializer( hadis = HadisListSerializer(
many=True, many=True,
read_only=True, read_only=True,
source='hadisreference_set'
source='hadis_references'
) )
class Meta: class Meta:
model = BookReference 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): class BookReferenceSyncSerializer(serializers.ModelSerializer):
"""Serializer for syncing all book reference data for offline mode""" """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 # Basic information
detail = serializers.SerializerMethodField() detail = serializers.SerializerMethodField()
# Hadis group (related hadises) # Hadis group (related hadises)
hadises = serializers.SerializerMethodField() hadises = serializers.SerializerMethodField()
authors = serializers.SerializerMethodField()
# authors = serializers.SerializerMethodField()
class Meta: class Meta:
model = BookReference model = BookReference
fields = [ fields = [
'id', 'title','rate' , 'authors' ,'detail', 'hadises'
'id', 'title','rate' , 'author' ,'detail','image','attribute', 'hadises'
] ]
def get_authors(self,obj): def get_authors(self,obj):
authors = [] authors = []
@ -102,6 +119,7 @@ class BookReferenceSyncSerializer(serializers.ModelSerializer):
'volume': obj.volume, 'volume': obj.volume,
'language': obj.language, 'language': obj.language,
'isbn': obj.isbn, 'isbn': obj.isbn,
'number_page':obj.number_page,
'year_of_publication': obj.year_of_publication, 'year_of_publication': obj.year_of_publication,
'number_of_pages': obj.number_page, 'number_of_pages': obj.number_page,
'volume_info': obj.volume, '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 django.shortcuts import get_object_or_404
from utils.pagination import NoPagination from utils.pagination import NoPagination
from rest_framework.response import Response 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 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( return Hadis.objects.filter(
category_id=category_id, category_id=category_id,
status=True 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): class HadisBasicView(RetrieveAPIView):
@ -116,48 +123,39 @@ class HadisDetailView(RetrieveAPIView):
'references__book_reference__description', 'references__book_reference__description',
) )
class HadisTransmittersView(RetrieveAPIView): 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' lookup_url_kwarg = 'hadis_id'
@hadis_transmitters_swagger @hadis_transmitters_swagger
def get(self, request, *args, **kwargs): 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): 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): class HadisCorrectionsView(RetrieveAPIView):
""" """

43
apps/hadis/views/reference.py

@ -35,8 +35,8 @@ class BookDetailView(RetrieveAPIView):
def get_queryset(self): def get_queryset(self):
return BookReference.objects.filter(id = self.kwargs['bookreference_id']).prefetch_related( 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' 'hadis_references__hadis'
).order_by('id') ).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() queryset = BookAttribute.objects.all()
serializer_class = BookAttributeSerializer 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