diff --git a/apps/hadis/migrations/0018_bookauthor_features_bookauthor_known_as_and_more.py b/apps/hadis/migrations/0018_bookauthor_features_bookauthor_known_as_and_more.py new file mode 100644 index 0000000..f3f5105 --- /dev/null +++ b/apps/hadis/migrations/0018_bookauthor_features_bookauthor_known_as_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 5.2.12 on 2026-06-21 18:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('hadis', '0017_booktag_alter_bookreference_subject_area_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='bookauthor', + name='features', + field=models.JSONField(blank=True, default=list, verbose_name='Features'), + ), + migrations.AddField( + model_name='bookauthor', + name='known_as', + field=models.JSONField(blank=True, default=list, verbose_name='Known as'), + ), + migrations.AddField( + model_name='bookauthor', + name='rutba', + field=models.JSONField(blank=True, default=list, verbose_name='Rutba'), + ), + migrations.AddField( + model_name='bookauthor', + name='scholarity', + field=models.JSONField(blank=True, default=list, verbose_name='Scholarity'), + ), + migrations.AddField( + model_name='bookauthor', + name='tabaqa', + field=models.JSONField(blank=True, default=list, verbose_name='Tabaqa'), + ), + ] diff --git a/apps/hadis/models/reference.py b/apps/hadis/models/reference.py index 6eb0e3f..db65997 100644 --- a/apps/hadis/models/reference.py +++ b/apps/hadis/models/reference.py @@ -354,12 +354,21 @@ class BookAuthor(models.Model): """ Model for book reference authors """ - name = models.JSONField(default = list , verbose_name=_('Name')) + name = models.JSONField(default=list, verbose_name=_('Name')) slug = models.SlugField(max_length=255, verbose_name=_('slug'), null=True, blank=True, unique=True) + + # 👇 فیلدهای جدید اضافه شده + known_as = models.JSONField(default=list, blank=True, verbose_name=_('Known as')) + scholarity = models.JSONField(default=list, blank=True, verbose_name=_('Scholarity')) + tabaqa = models.JSONField(default=list, blank=True, verbose_name=_('Tabaqa')) + rutba = models.JSONField(default=list, blank=True, verbose_name=_('Rutba')) + features = models.JSONField(default=list, blank=True, verbose_name=_('Features')) + birth_year_hijri = models.IntegerField(verbose_name=_("Birth Year (Hijri)"), null=True, blank=True) death_year_hijri = models.IntegerField(verbose_name=_("Death Year (Hijri)"), null=True, blank=True) birth_year_miladi = models.IntegerField(verbose_name=_("Birth Year (Miladi)"), null=True, blank=True) death_year_miladi = models.IntegerField(verbose_name=_("Death Year (Miladi)"), null=True, blank=True) + book_references = models.ManyToManyField( BookReference, related_name='authors', diff --git a/apps/hadis/serializers/reference_v2.py b/apps/hadis/serializers/reference_v2.py index 4c4b249..427903c 100644 --- a/apps/hadis/serializers/reference_v2.py +++ b/apps/hadis/serializers/reference_v2.py @@ -183,3 +183,100 @@ class BookReferenceV2SyncSerializer(serializers.ModelSerializer): "editions_volumes": [], "all_volumes": BookVolumeV2Serializer(obj.volumes.all(), many=True).data } + +from apps.hadis.models.reference import BookAuthor, BookReference +from .category import LocalizedField + +class BookAuthorListSerializer(serializers.ModelSerializer): + """سریالایزر برای لیست نویسنده‌ها""" + name = LocalizedField() + + class Meta: + model = BookAuthor + fields = ['id', 'slug', 'name', 'birth_year_hijri', 'birth_year_miladi'] + + +class BookAuthorDetailSerializer(serializers.ModelSerializer): + """سریالایزر برای دیتیل یک نویسنده""" + name = LocalizedField() + known_as = LocalizedField() + scholarity = LocalizedField() + tabaqa = LocalizedField() + rutba = LocalizedField() + features = LocalizedField() + + class Meta: + model = BookAuthor + fields = [ + 'id', 'slug', 'name', 'known_as', 'scholarity', + 'tabaqa', 'rutba', 'features', + 'birth_year_hijri', 'death_year_hijri', + 'birth_year_miladi', 'death_year_miladi' + ] + + +class AuthorBookReferenceSerializer(serializers.ModelSerializer): + """سریالایزر برای لیست رفرنس‌های (کتاب‌های) یک نویسنده""" + title = LocalizedField() + authors = serializers.SerializerMethodField() + thumbnail = serializers.SerializerMethodField() + + class Meta: + model = BookReference + fields = ['id', 'slug', 'title', 'thumbnail', 'authors'] + + def get_authors(self, obj): + field = LocalizedField() + field._context = self.context + return [field.to_representation(author.name) for author in obj.authors.all()] + + def get_thumbnail(self, obj): + # گرفتن اولین عکس آپلود شده برای این رفرنس + first_image = obj.images.order_by('order').first() + if first_image and first_image.image: + request = self.context.get('request') + if request: + return request.build_absolute_uri(first_image.image.url) + return first_image.image.url + return None + +# یک سریالایزر مینیمال برای کتاب‌ها داخل سینک +class BookAuthorSyncReferenceSerializer(serializers.ModelSerializer): + title = LocalizedField() + + class Meta: + model = BookReference + fields = ['id', 'slug', 'title'] + +class BookAuthorSyncSerializer(serializers.ModelSerializer): + """ + سریالایزر برای سینک آفلاین تمام نویسنده‌ها. + شامل تمام اطلاعات و لیست کتاب‌های متصل به این نویسنده. + """ + name = LocalizedField() + known_as = LocalizedField() + scholarity = LocalizedField() + tabaqa = LocalizedField() + rutba = LocalizedField() + features = LocalizedField() + + # اضافه کردن لیست کتاب‌ها + books = BookAuthorSyncReferenceSerializer(source='book_references', many=True, read_only=True) + + class Meta: + model = BookAuthor + fields = [ + 'id', + 'slug', + 'name', + 'known_as', + 'scholarity', + 'tabaqa', + 'rutba', + 'features', + 'birth_year_hijri', + 'death_year_hijri', + 'birth_year_miladi', + 'death_year_miladi', + 'books' # 👈 لیست کتاب‌های هر نویسنده + ] \ No newline at end of file diff --git a/apps/hadis/urls.py b/apps/hadis/urls.py index c5e4b29..1195c2e 100644 --- a/apps/hadis/urls.py +++ b/apps/hadis/urls.py @@ -14,6 +14,7 @@ from .views_admin import ( DovodiNarratorLayerViewSet, DovodiTransmitterReliabilityViewSet, DovodiOpinionStatusViewSet, DovodiHadisSectViewSet, DovodiHadisCategoryViewSet ) +from .views.reference_v2 import BookAuthorListView, BookAuthorDetailView, AuthorReferencesListView, BookAuthorSyncView from django.views.decorators.cache import cache_page from django.views.decorators.vary import vary_on_headers @@ -51,6 +52,13 @@ for prefix, viewset, basename in admin_router.registry: viewset.swagger_schema = None urlpatterns = [ + + path('sync/authors/', BookAuthorSyncView.as_view(), name='sync-author-list'), + path('authors/', BookAuthorListView.as_view(), name='author-list'), + path('authors//', BookAuthorDetailView.as_view(), name='author-detail'), + path('authors//references/', AuthorReferencesListView.as_view(), name='author-references'), + + # Admin endpoints path('admin/', include(admin_router.urls)), diff --git a/apps/hadis/views/reference_v2.py b/apps/hadis/views/reference_v2.py index 82262db..ee5a382 100644 --- a/apps/hadis/views/reference_v2.py +++ b/apps/hadis/views/reference_v2.py @@ -199,3 +199,71 @@ class BookReferenceV2ListView(generics.ListAPIView): "filters": filters_metadata, "results": serializer.data }) + + +from rest_framework.generics import ListAPIView, RetrieveAPIView +from rest_framework.permissions import AllowAny +from utils.pagination import StandardResultsSetPagination +from apps.hadis.models.reference import BookAuthor, BookReference +from apps.hadis.serializers.reference_v2 import ( + BookAuthorListSerializer, + BookAuthorDetailSerializer, + AuthorBookReferenceSerializer, + BookAuthorSyncSerializer +) + +class BookAuthorListView(ListAPIView): + """ + لیست تمام نویسندگان رفرنس + """ + queryset = BookAuthor.objects.all().order_by('-created_at') + serializer_class = BookAuthorListSerializer + pagination_class = StandardResultsSetPagination + permission_classes = [AllowAny] + + +class BookAuthorDetailView(RetrieveAPIView): + """ + دریافت اطلاعات دقیق یک نویسنده بر اساس slug + """ + queryset = BookAuthor.objects.all() + serializer_class = BookAuthorDetailSerializer + lookup_field = 'slug' + lookup_url_kwarg = 'author_slug' + permission_classes = [AllowAny] + + +class AuthorReferencesListView(ListAPIView): + """ + لیست تمام رفرنس‌ها (کتاب‌ها)یی که این نویسنده در آن‌ها تگ شده است + """ + serializer_class = AuthorBookReferenceSerializer + pagination_class = StandardResultsSetPagination + permission_classes = [AllowAny] + + def get_queryset(self): + author_slug = self.kwargs.get('author_slug') + + # پیدا کردن رفرنس‌هایی که این نویسنده جزو authors آن‌هاست + return BookReference.objects.filter( + authors__slug=author_slug + ).prefetch_related('authors', 'images').distinct().order_by('-created_at') + +class BookAuthorSyncView(generics.ListAPIView): + """ + API view to sync all Book Authors data for offline mode. + Returns complete author data and their associated books without pagination. + """ + # 👇 استفاده از prefetch_related برای جلوگیری از کرش کردن دیتابیس به خاطر N+1 Query + queryset = BookAuthor.objects.prefetch_related('book_references').order_by('id') + serializer_class = BookAuthorSyncSerializer + pagination_class = NoPagination + permission_classes = [AllowAny] + + @swagger_auto_schema( + operation_summary="Sync Book Authors", + operation_description="Returns complete book authors data including their books for offline sync.", + tags=['Dobodbi - Hadis (V2)'], + ) + def get(self, request, *args, **kwargs): + return super().get(request, *args, **kwargs) \ No newline at end of file