5 Commits

  1. 12
      apps/article/serializers_admin.py
  2. 4
      apps/bookmark/apps.py
  3. 89
      apps/bookmark/management/commands/cleanup_orphan_bookmarks.py
  4. 8
      apps/bookmark/models/bookmark.py
  5. 83
      apps/bookmark/signals.py
  6. 25
      apps/bookmark/views/bookmark.py
  7. 62
      apps/hadis/serializers/serializers_admin.py
  8. 4
      apps/hadis/views_admin.py
  9. 12
      apps/library/serializers.py
  10. 4
      apps/library/serializers_admin.py
  11. 3
      apps/library/urls.py
  12. 4
      apps/library/views.py
  13. 9
      apps/video/serializers.py

12
apps/article/serializers_admin.py

@ -12,18 +12,14 @@ class AbsoluteImageField(serializers.ImageField):
if not uploaded: if not uploaded:
return uploaded return uploaded
from utils.watermark import apply_watermark_to_upload
watermarked = apply_watermark_to_upload(uploaded, position="bottom_left")
target_file = watermarked or uploaded
compressed_bytes = maybe_compress_uploaded_file(target_file)
compressed_bytes = maybe_compress_uploaded_file(uploaded)
if compressed_bytes is None: if compressed_bytes is None:
return target_file
return uploaded
return SimpleUploadedFile( return SimpleUploadedFile(
name=getattr(target_file, "name", "image"),
name=getattr(uploaded, "name", "image"),
content=compressed_bytes, content=compressed_bytes,
content_type=getattr(target_file, "content_type", None),
content_type=getattr(uploaded, "content_type", None),
) )
def to_representation(self, value): def to_representation(self, value):

4
apps/bookmark/apps.py

@ -4,3 +4,7 @@ from django.apps import AppConfig
class BookmarkConfig(AppConfig): class BookmarkConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField' default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.bookmark' name = 'apps.bookmark'
def ready(self):
import apps.bookmark.signals # noqa: F401

89
apps/bookmark/management/commands/cleanup_orphan_bookmarks.py

@ -0,0 +1,89 @@
import logging
from django.core.management.base import BaseCommand
from apps.bookmark.models import Bookmark
logger = logging.getLogger(__name__)
class Command(BaseCommand):
help = "Find and delete orphan bookmarks whose target content was deleted from the database."
def add_arguments(self, parser):
parser.add_argument(
'--dry-run',
action='store_true',
help='Scan and report orphan bookmarks without actually deleting them.',
)
def handle(self, *args, **options):
dry_run = options.get('dry_run', False)
from apps.library.models import Book
from apps.podcast.models import Podcast, PodcastPlaylist
from apps.video.models import Video, VideoPlaylist
from apps.hadis.models import Hadis, HadisCorrection
from apps.article.models import Article
service_model_map = {
Bookmark.ServiceChoices.LIBRARY: (Book, 'Library Book'),
Bookmark.ServiceChoices.PODCAST: (Podcast, 'Podcast'),
Bookmark.ServiceChoices.PODCAST_PLAYLIST: (PodcastPlaylist, 'Podcast Playlist'),
Bookmark.ServiceChoices.HADITH: (Hadis, 'Hadith'),
Bookmark.ServiceChoices.HADITH_CORRECTION: (HadisCorrection, 'Hadith Correction'),
Bookmark.ServiceChoices.VIDEO: (Video, 'Video'),
Bookmark.ServiceChoices.VIDEO_PLAYLIST: (VideoPlaylist, 'Video Playlist'),
Bookmark.ServiceChoices.ARTICLE: (Article, 'Article'),
}
total_checked = Bookmark.objects.count()
self.stdout.write(f"Scanning {total_checked} total bookmark(s)...")
total_orphans = 0
orphan_ids_to_delete = []
for service_choice, (model_cls, label) in service_model_map.items():
bms = Bookmark.objects.filter(service=service_choice)
content_ids = set(bms.values_list('content_id', flat=True))
if not content_ids:
continue
if service_choice == Bookmark.ServiceChoices.VIDEO:
existing_ids = set(Video.objects.filter(id__in=content_ids).values_list('id', flat=True)) | set(VideoPlaylist.objects.filter(id__in=content_ids).values_list('id', flat=True))
elif service_choice == Bookmark.ServiceChoices.PODCAST:
existing_ids = set(Podcast.objects.filter(id__in=content_ids).values_list('id', flat=True)) | set(PodcastPlaylist.objects.filter(id__in=content_ids).values_list('id', flat=True))
else:
existing_ids = set(model_cls.objects.filter(id__in=content_ids).values_list('id', flat=True))
missing_ids = content_ids - existing_ids
if missing_ids:
orphan_bms = bms.filter(content_id__in=missing_ids)
count = orphan_bms.count()
total_orphans += count
ids = list(orphan_bms.values_list('id', flat=True))
orphan_ids_to_delete.extend(ids)
self.stdout.write(
self.style.WARNING(f" - [{label} ({service_choice})]: found {count} orphan bookmark(s) (missing IDs: {sorted(list(missing_ids))[:10]}{'...' if len(missing_ids) > 10 else ''})")
)
# Check any bookmarks with unknown service choices
known_services = set(service_model_map.keys())
unknown_bms = Bookmark.objects.exclude(service__in=known_services)
unknown_count = unknown_bms.count()
if unknown_count > 0:
total_orphans += unknown_count
orphan_ids_to_delete.extend(list(unknown_bms.values_list('id', flat=True)))
self.stdout.write(
self.style.WARNING(f" - [Unknown Service]: found {unknown_count} bookmark(s) with invalid service name.")
)
if total_orphans == 0:
self.stdout.write(self.style.SUCCESS("No orphan bookmarks found! Everything is clean."))
return
self.stdout.write(f"\nTotal orphan bookmark(s) found: {total_orphans}")
if dry_run:
self.stdout.write(self.style.NOTICE("Dry run enabled. No bookmarks were deleted."))
else:
deleted_count, _ = Bookmark.objects.filter(id__in=orphan_ids_to_delete).delete()
self.stdout.write(self.style.SUCCESS(f"Successfully deleted {deleted_count} orphan bookmark(s)!"))

8
apps/bookmark/models/bookmark.py

@ -71,8 +71,8 @@ class Bookmark(models.Model):
from apps.library.models import Book from apps.library.models import Book
return Book.objects.filter(id=content_id).exists() return Book.objects.filter(id=content_id).exists()
elif service == cls.ServiceChoices.PODCAST: elif service == cls.ServiceChoices.PODCAST:
from apps.podcast.models import Podcast
return Podcast.objects.filter(id=content_id).exists()
from apps.podcast.models import Podcast, PodcastPlaylist
return Podcast.objects.filter(id=content_id).exists() or PodcastPlaylist.objects.filter(id=content_id).exists()
elif service == cls.ServiceChoices.PODCAST_PLAYLIST: elif service == cls.ServiceChoices.PODCAST_PLAYLIST:
from apps.podcast.models import PodcastPlaylist from apps.podcast.models import PodcastPlaylist
return PodcastPlaylist.objects.filter(id=content_id).exists() return PodcastPlaylist.objects.filter(id=content_id).exists()
@ -83,8 +83,8 @@ class Bookmark(models.Model):
from apps.hadis.models import HadisCorrection from apps.hadis.models import HadisCorrection
return HadisCorrection.objects.filter(id=content_id).exists() return HadisCorrection.objects.filter(id=content_id).exists()
elif service == cls.ServiceChoices.VIDEO: elif service == cls.ServiceChoices.VIDEO:
from apps.video.models import Video
return Video.objects.filter(id=content_id).exists()
from apps.video.models import Video, VideoPlaylist
return Video.objects.filter(id=content_id).exists() or VideoPlaylist.objects.filter(id=content_id).exists()
elif service == cls.ServiceChoices.VIDEO_PLAYLIST: elif service == cls.ServiceChoices.VIDEO_PLAYLIST:
from apps.video.models import VideoPlaylist from apps.video.models import VideoPlaylist
return VideoPlaylist.objects.filter(id=content_id).exists() return VideoPlaylist.objects.filter(id=content_id).exists()

83
apps/bookmark/signals.py

@ -0,0 +1,83 @@
import logging
from django.db.models.signals import post_delete
from django.dispatch import receiver
from apps.bookmark.models import Bookmark
logger = logging.getLogger(__name__)
def _delete_bookmarks_for(service: str, content_id: int):
if not content_id:
return
try:
deleted_count, _ = Bookmark.objects.filter(service=service, content_id=content_id).delete()
if deleted_count > 0:
logger.info("[Bookmark] Auto-deleted %d bookmark(s) for %s ID %s upon content deletion.", deleted_count, service, content_id)
except Exception as exc:
logger.error("[Bookmark] Error deleting bookmarks for %s ID %s: %s", service, content_id, exc)
# 1. Library Books
try:
from apps.library.models import Book
@receiver(post_delete, sender=Book)
def on_book_deleted(sender, instance, **kwargs):
_delete_bookmarks_for(Bookmark.ServiceChoices.LIBRARY, instance.id)
except ImportError:
pass
# 2. Hadith & Hadith Corrections
try:
from apps.hadis.models import Hadis, HadisCorrection
@receiver(post_delete, sender=Hadis)
def on_hadis_deleted(sender, instance, **kwargs):
_delete_bookmarks_for(Bookmark.ServiceChoices.HADITH, instance.id)
@receiver(post_delete, sender=HadisCorrection)
def on_hadis_correction_deleted(sender, instance, **kwargs):
_delete_bookmarks_for(Bookmark.ServiceChoices.HADITH_CORRECTION, instance.id)
except ImportError:
pass
# 3. Videos & Video Playlists
try:
from apps.video.models import Video, VideoPlaylist
@receiver(post_delete, sender=Video)
def on_video_deleted(sender, instance, **kwargs):
_delete_bookmarks_for(Bookmark.ServiceChoices.VIDEO, instance.id)
@receiver(post_delete, sender=VideoPlaylist)
def on_video_playlist_deleted(sender, instance, **kwargs):
_delete_bookmarks_for(Bookmark.ServiceChoices.VIDEO_PLAYLIST, instance.id)
_delete_bookmarks_for(Bookmark.ServiceChoices.VIDEO, instance.id)
except ImportError:
pass
# 4. Podcasts & Podcast Playlists
try:
from apps.podcast.models import Podcast, PodcastPlaylist
@receiver(post_delete, sender=Podcast)
def on_podcast_deleted(sender, instance, **kwargs):
_delete_bookmarks_for(Bookmark.ServiceChoices.PODCAST, instance.id)
@receiver(post_delete, sender=PodcastPlaylist)
def on_podcast_playlist_deleted(sender, instance, **kwargs):
_delete_bookmarks_for(Bookmark.ServiceChoices.PODCAST_PLAYLIST, instance.id)
_delete_bookmarks_for(Bookmark.ServiceChoices.PODCAST, instance.id)
except ImportError:
pass
# 5. Articles
try:
from apps.article.models import Article
@receiver(post_delete, sender=Article)
def on_article_deleted(sender, instance, **kwargs):
_delete_bookmarks_for(Bookmark.ServiceChoices.ARTICLE, instance.id)
except ImportError:
pass

25
apps/bookmark/views/bookmark.py

@ -274,8 +274,10 @@ def _get_valid_content_ids(service, content_ids):
from apps.video.models import VideoPlaylist from apps.video.models import VideoPlaylist
return set(VideoPlaylist.objects.filter(id__in=content_ids, status=True).values_list('id', flat=True)) return set(VideoPlaylist.objects.filter(id__in=content_ids, status=True).values_list('id', flat=True))
elif service in ('video', 'video_item'): elif service in ('video', 'video_item'):
from apps.video.models import Video
return set(Video.objects.filter(id__in=content_ids, status=True).values_list('id', flat=True))
from apps.video.models import Video, VideoPlaylist
v_ids = set(Video.objects.filter(id__in=content_ids, status=True).values_list('id', flat=True))
pl_ids = set(VideoPlaylist.objects.filter(id__in=content_ids, status=True).values_list('id', flat=True))
return v_ids | pl_ids
elif service == 'podcast': elif service == 'podcast':
from apps.podcast.models import Podcast, PodcastPlaylist from apps.podcast.models import Podcast, PodcastPlaylist
p_ids = set(Podcast.objects.filter(id__in=content_ids, status=True).values_list('id', flat=True)) p_ids = set(Podcast.objects.filter(id__in=content_ids, status=True).values_list('id', flat=True))
@ -287,12 +289,16 @@ def _get_valid_content_ids(service, content_ids):
elif service in ('article', 'blog'): elif service in ('article', 'blog'):
from apps.article.models import Article from apps.article.models import Article
return set(Article.objects.filter(id__in=content_ids, status=True).values_list('id', flat=True)) return set(Article.objects.filter(id__in=content_ids, status=True).values_list('id', flat=True))
elif service in ('hadith', 'hadith_correction'):
from apps.hadith.models import Hadith
return set(Hadith.objects.filter(id__in=content_ids, status=True).values_list('id', flat=True))
except Exception:
pass
return set(content_ids)
elif service == 'hadith':
from apps.hadis.models import Hadis
return set(Hadis.objects.filter(id__in=content_ids, status=True).values_list('id', flat=True))
elif service == 'hadith_correction':
from apps.hadis.models import HadisCorrection
return set(HadisCorrection.objects.filter(id__in=content_ids).values_list('id', flat=True))
except Exception as exc:
import logging
logging.getLogger(__name__).warning("[Bookmark] Error validating content ids for service %s: %s", service, exc)
return set()
class BookmarkStatusView(APIView): class BookmarkStatusView(APIView):
@ -386,8 +392,7 @@ class BookmarkListView(APIView):
if b.content_id in valid_ids: if b.content_id in valid_ids:
valid_bookmarks.append(b) valid_bookmarks.append(b)
else: else:
b.status = False
b.save(update_fields=['status'])
b.delete()
serializer = BookmarkSerializer(valid_bookmarks, many=True) serializer = BookmarkSerializer(valid_bookmarks, many=True)
return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.data, status=status.HTTP_200_OK)

62
apps/hadis/serializers/serializers_admin.py

@ -39,18 +39,14 @@ class AbsoluteImageField(serializers.ImageField):
if not uploaded: if not uploaded:
return uploaded return uploaded
from utils.watermark import apply_watermark_to_upload
watermarked = apply_watermark_to_upload(uploaded, position="bottom_left")
target_file = watermarked or uploaded
compressed_bytes = maybe_compress_uploaded_file(target_file)
compressed_bytes = maybe_compress_uploaded_file(uploaded)
if compressed_bytes is None: if compressed_bytes is None:
return target_file
return uploaded
return SimpleUploadedFile( return SimpleUploadedFile(
name=getattr(target_file, "name", "image"),
name=getattr(uploaded, "name", "image"),
content=compressed_bytes, content=compressed_bytes,
content_type=getattr(target_file, "content_type", None),
content_type=getattr(uploaded, "content_type", None),
) )
def to_representation(self, value): def to_representation(self, value):
@ -1503,6 +1499,7 @@ class AdminBookReferenceListSerializer(serializers.ModelSerializer):
volume = serializers.SerializerMethodField() volume = serializers.SerializerMethodField()
publisher = serializers.SerializerMethodField() publisher = serializers.SerializerMethodField()
year_of_publication = serializers.SerializerMethodField() year_of_publication = serializers.SerializerMethodField()
hadis_count = serializers.SerializerMethodField()
thumbnail = serializers.SerializerMethodField() thumbnail = serializers.SerializerMethodField()
image = serializers.SerializerMethodField() image = serializers.SerializerMethodField()
@ -1521,11 +1518,19 @@ class AdminBookReferenceListSerializer(serializers.ModelSerializer):
"publisher", "publisher",
"year_of_publication", "year_of_publication",
"rate", "rate",
"hadis_count",
"thumbnail", "thumbnail",
"image", "image",
"created_at", "created_at",
] ]
def get_hadis_count(self, obj):
if hasattr(obj, "annotated_hadis_count"):
return obj.annotated_hadis_count
if not obj or not obj.pk:
return 0
return Hadis.objects.filter(references__book_reference=obj).distinct().count()
def get_thumbnail(self, obj): def get_thumbnail(self, obj):
first_img = obj.images.filter(book_volume__isnull=True).order_by("order", "id").first() first_img = obj.images.filter(book_volume__isnull=True).order_by("order", "id").first()
if first_img and first_img.image: if first_img and first_img.image:
@ -1593,6 +1598,8 @@ class AdminBookReferenceDetailSerializer(serializers.ModelSerializer):
city_of_publication = serializers.SerializerMethodField() city_of_publication = serializers.SerializerMethodField()
country_of_publication = serializers.SerializerMethodField() country_of_publication = serializers.SerializerMethodField()
edition_number = serializers.SerializerMethodField() edition_number = serializers.SerializerMethodField()
hadis_count = serializers.SerializerMethodField()
child_arguments = serializers.SerializerMethodField()
thumbnail = serializers.SerializerMethodField() thumbnail = serializers.SerializerMethodField()
image = serializers.SerializerMethodField() image = serializers.SerializerMethodField()
@ -1653,6 +1660,41 @@ class AdminBookReferenceDetailSerializer(serializers.ModelSerializer):
ed = obj.editions.first() ed = obj.editions.first()
return ed.edition_number if ed else "" return ed.edition_number if ed else ""
def get_hadis_count(self, obj):
if hasattr(obj, "annotated_hadis_count"):
return obj.annotated_hadis_count
if not obj or not obj.pk:
return 0
return Hadis.objects.filter(references__book_reference=obj).distinct().count()
def get_child_arguments(self, obj):
if not obj or not obj.pk:
return []
hadises = (
Hadis.objects.filter(references__book_reference=obj)
.select_related("hadis_status", "category")
.distinct()
.order_by("number", "id")
)
return [
{
"id": h.id,
"number": h.number,
"title": h.title,
"title_narrator": h.title_narrator,
"slug": h.slug,
"status": h.status,
"source_type": h.category.source_type if h.category else "hadith",
"status_detail": {
"id": h.hadis_status.id,
"title": h.hadis_status.title,
"color": h.hadis_status.color,
"main_color_code": getattr(h.hadis_status, "main_color_code", None),
} if h.hadis_status else None,
}
for h in hadises
]
class Meta: class Meta:
model = BookReference model = BookReference
fields = [ fields = [
@ -1683,6 +1725,8 @@ class AdminBookReferenceDetailSerializer(serializers.ModelSerializer):
"rate", "rate",
"author", "author",
"author_detail", "author_detail",
"hadis_count",
"child_arguments",
"thumbnail", "thumbnail",
"image", "image",
"remove_thumbnail", "remove_thumbnail",
@ -1691,7 +1735,7 @@ class AdminBookReferenceDetailSerializer(serializers.ModelSerializer):
"updated_at", "updated_at",
"share_link", "share_link",
] ]
read_only_fields = ["id", "slug", "created_at", "updated_at", "share_link"]
read_only_fields = ["id", "slug", "hadis_count", "child_arguments", "created_at", "updated_at", "share_link"]
def to_internal_value(self, data): def to_internal_value(self, data):
import json import json

4
apps/hadis/views_admin.py

@ -1,4 +1,4 @@
from django.db.models import Q, Exists, OuterRef
from django.db.models import Q, Exists, OuterRef, Count
from rest_framework.authentication import TokenAuthentication from rest_framework.authentication import TokenAuthentication
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.parsers import FormParser, JSONParser, MultiPartParser from rest_framework.parsers import FormParser, JSONParser, MultiPartParser
@ -714,7 +714,7 @@ class AdminBookReferenceViewSet(ModelViewSet):
return AdminBookReferenceDetailSerializer return AdminBookReferenceDetailSerializer
def get_queryset(self): def get_queryset(self):
queryset = BookReference.objects.all().prefetch_related("subject_area", "volumes", "editions").select_related("type", "author")
queryset = BookReference.objects.all().annotate(annotated_hadis_count=Count("hadis_references__hadis", distinct=True)).prefetch_related("subject_area", "volumes", "editions").select_related("type", "author")
search_query = self.request.query_params.get("search") search_query = self.request.query_params.get("search")
if search_query: if search_query:

12
apps/library/serializers.py

@ -134,6 +134,8 @@ class BookSerializer(serializers.ModelSerializer):
share_link = serializers.CharField(read_only=True) share_link = serializers.CharField(read_only=True)
author = serializers.SerializerMethodField() author = serializers.SerializerMethodField()
author_detail = AuthorSummarySerializer(source='author', read_only=True) author_detail = AuthorSummarySerializer(source='author', read_only=True)
categories = CategorySerializer(many=True, read_only=True)
category = serializers.SerializerMethodField()
language = serializers.SlugRelatedField( language = serializers.SlugRelatedField(
slug_field='code', slug_field='code',
queryset=Language.objects.all(), queryset=Language.objects.all(),
@ -149,8 +151,8 @@ class BookSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Book model = Book
fields = ( fields = (
'id', 'title', 'slug', 'summary', 'summary_title', 'thumbnail', 'slogan',
'status', 'pin', 'view_count', 'download_count', 'publisher', 'year_of_publication', 'author', 'author_detail', 'isbn', 'numnber_of_volume',
'id', 'title', 'slug', 'summary', 'summary_title', 'description', 'thumbnail', 'slogan',
'status', 'pin', 'view_count', 'download_count', 'publisher', 'year_of_publication', 'author', 'author_detail', 'categories', 'category', 'isbn', 'numnber_of_volume',
'language', 'main_themes', 'notable_works', 'language', 'main_themes', 'notable_works',
'file_type', 'book_file', 'created_at', 'bookmark', 'user_rate', 'file_type', 'book_file', 'created_at', 'bookmark', 'user_rate',
'average_rate', 'share_link' 'average_rate', 'share_link'
@ -159,6 +161,12 @@ class BookSerializer(serializers.ModelSerializer):
def get_author(self, obj): def get_author(self, obj):
return obj.author.full_name if obj.author_id else None return obj.author.full_name if obj.author_id else None
def get_category(self, obj):
first_cat = obj.categories.filter(status=True).first() or obj.categories.first()
if first_cat:
return CategorySerializer(first_cat, context=self.context).data
return None
def get_bookmark(self, obj): def get_bookmark(self, obj):
""" """
Get bookmark information for this book. Get bookmark information for this book.

4
apps/library/serializers_admin.py

@ -253,7 +253,7 @@ class AdminBookDetailSerializer(serializers.ModelSerializer):
collections = validated_data.pop("collections", []) collections = validated_data.pop("collections", [])
book = super().create(validated_data) book = super().create(validated_data)
if categories: if categories:
book.categories.set(categories)
book.categories.set(categories[:1] if len(categories) > 1 else categories)
if collections: if collections:
book.collections.set(collections) book.collections.set(collections)
return book return book
@ -274,7 +274,7 @@ class AdminBookDetailSerializer(serializers.ModelSerializer):
book = super().update(instance, validated_data) book = super().update(instance, validated_data)
if categories is not None: if categories is not None:
book.categories.set(categories)
book.categories.set(categories[:1] if len(categories) > 1 else categories)
if collections is not None: if collections is not None:
book.collections.set(collections) book.collections.set(collections)
return book return book

3
apps/library/urls.py

@ -39,10 +39,11 @@ admin_router.register(r'collections', AdminLibraryCollectionViewSet, basename='a
admin_router.register(r'dovodi-collections', DovodiAdminLibraryCollectionViewSet, basename='admin-library-dovodi-collections') admin_router.register(r'dovodi-collections', DovodiAdminLibraryCollectionViewSet, basename='admin-library-dovodi-collections')
dovodi_router = SimpleRouter() dovodi_router = SimpleRouter()
dovodi_router.register(r'categories', DovodiAdminLibraryCategoryViewSet, basename='dovodi-library-categories')
dovodi_router.register(r'collections', DovodiAdminLibraryCollectionViewSet, basename='dovodi-library-collections') dovodi_router.register(r'collections', DovodiAdminLibraryCollectionViewSet, basename='dovodi-library-collections')
# Hide admin viewsets from swagger # Hide admin viewsets from swagger
for prefix, viewset, basename in admin_router.registry:
for prefix, viewset, basename in admin_router.registry + dovodi_router.registry:
viewset.swagger_schema = None viewset.swagger_schema = None
urlpatterns = [ urlpatterns = [

4
apps/library/views.py

@ -120,7 +120,7 @@ class BookListView(ListAPIView):
return context return context
def get_queryset(self): def get_queryset(self):
queryset = Book.objects.filter(status=True).select_related('author', 'language')
queryset = Book.objects.filter(status=True).select_related('author', 'language').prefetch_related('categories')
# Filter by collection if provided # Filter by collection if provided
collection_id = self.request.query_params.get('collection_id') collection_id = self.request.query_params.get('collection_id')
@ -226,7 +226,7 @@ class BookDetailView(Gone410ViewMixin, CanonicalSlugViewSetMixin, RetrieveAPIVie
serializer_class = BookSerializer serializer_class = BookSerializer
permission_classes = (IsAuthenticated,) permission_classes = (IsAuthenticated,)
authentication_classes = [TokenAuthentication] authentication_classes = [TokenAuthentication]
queryset = Book.objects.all()
queryset = Book.objects.all().select_related('author').prefetch_related('categories')
lookup_field = 'slug' lookup_field = 'slug'
active_field_name = 'status' active_field_name = 'status'

9
apps/video/serializers.py

@ -118,12 +118,17 @@ class VideoPlaylistDetailSerializer(serializers.ModelSerializer):
"""Get bookmark information for this playlist.""" """Get bookmark information for this playlist."""
request = self.context.get('request') request = self.context.get('request')
user = request.user if request else None user = request.user if request else None
print(user.id)
book_mark = BookmarkStatusSerializer.get_bookmark_info( book_mark = BookmarkStatusSerializer.get_bookmark_info(
obj=obj, obj=obj,
user=user, user=user,
service='video_playlist'
service='video'
) )
if not book_mark.get('is_bookmarked', False):
book_mark = BookmarkStatusSerializer.get_bookmark_info(
obj=obj,
user=user,
service='video_playlist'
)
return book_mark.get('is_bookmarked', False) return book_mark.get('is_bookmarked', False)
def get_user_rate(self, obj): def get_user_rate(self, obj):

Loading…
Cancel
Save