diff --git a/apps/library/migrations/0009_alter_author_options.py b/apps/library/migrations/0009_alter_author_options.py new file mode 100644 index 0000000..a945dbe --- /dev/null +++ b/apps/library/migrations/0009_alter_author_options.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.30 on 2026-08-27 11:03 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('library', '0008_alter_author_slug_alter_book_slug_and_more'), + ] + + operations = [ + migrations.AlterModelOptions( + name='author', + options={'ordering': ['-created_at', '-id'], 'verbose_name': 'Author', 'verbose_name_plural': 'Authors'}, + ), + ] diff --git a/apps/library/models.py b/apps/library/models.py index c1898f6..ea5031d 100644 --- a/apps/library/models.py +++ b/apps/library/models.py @@ -122,7 +122,7 @@ class Author(LowercaseSlugMixin, models.Model): class Meta: verbose_name = _('Author') verbose_name_plural = _('Authors') - ordering = ['full_name', 'id'] + ordering = ['-created_at', '-id'] def __str__(self): return self.full_name diff --git a/apps/library/views.py b/apps/library/views.py index bbc585f..15f4f66 100644 --- a/apps/library/views.py +++ b/apps/library/views.py @@ -268,7 +268,7 @@ class AuthorListView(ListAPIView): def get_queryset(self): return Author.objects.annotate( books_count_annotation=Count('books', filter=Q(books__status=True)) - ).order_by('full_name', 'id') + ).order_by('-created_at', '-id') class AuthorDetailView(RetrieveAPIView): diff --git a/apps/library/views_admin.py b/apps/library/views_admin.py index 863f629..caa1144 100644 --- a/apps/library/views_admin.py +++ b/apps/library/views_admin.py @@ -133,7 +133,7 @@ class AdminAuthorViewSet(ModelViewSet): | Q(slug__icontains=search_query) ) - return queryset.order_by("full_name", "id") + return queryset.order_by("-created_at", "-id") @action(detail=True, methods=["post"], url_path="assign-books") def assign_books(self, request, pk=None): diff --git a/apps/podcast/views_admin.py b/apps/podcast/views_admin.py index 83be09a..c8adf93 100644 --- a/apps/podcast/views_admin.py +++ b/apps/podcast/views_admin.py @@ -211,3 +211,39 @@ class AdminPodcastPlaylistViewSet(ModelViewSet): context={"request": request}, ) return Response(serializer.data) + + @action(detail=True, methods=["post"], url_path="assign-items") + def assign_items(self, request, pk=None): + playlist = self.get_object() + add_ids = request.data.get("add_ids", []) or request.data.get("podcast_ids", []) or request.data.get("item_ids", []) + remove_ids = request.data.get("remove_ids", []) + + with transaction.atomic(): + if remove_ids: + playlist.playlist_items.filter(id__in=remove_ids).delete() + + if add_ids: + existing_podcast_ids = set(playlist.playlist_items.values_list("podcast_id", flat=True)) + new_podcast_ids = [pid for pid in add_ids if pid not in existing_podcast_ids] + current_count = playlist.playlist_items.count() + new_items = [ + PlaylistItem( + playlist=playlist, + podcast_id=pid, + priority=current_count + idx + 1, + ) + for idx, pid in enumerate(new_podcast_ids) + ] + if new_items: + PlaylistItem.objects.bulk_create(new_items) + + playlist.total_time = playlist.calculate_total_time() + playlist.save(update_fields=["total_time"]) + + serializer = AdminPodcastPlaylistItemSerializer( + playlist.playlist_items.select_related("podcast").order_by("priority", "id"), + many=True, + context={"request": request}, + ) + return Response(serializer.data, status=status.HTTP_200_OK) + diff --git a/apps/video/views_admin.py b/apps/video/views_admin.py index 2f7f601..061ee9a 100644 --- a/apps/video/views_admin.py +++ b/apps/video/views_admin.py @@ -208,3 +208,39 @@ class AdminVideoPlaylistViewSet(ModelViewSet): context={"request": request}, ) return Response(serializer.data) + + @action(detail=True, methods=["post"], url_path="assign-items") + def assign_items(self, request, pk=None): + playlist = self.get_object() + add_ids = request.data.get("add_ids", []) or request.data.get("video_ids", []) or request.data.get("item_ids", []) + remove_ids = request.data.get("remove_ids", []) + + with transaction.atomic(): + if remove_ids: + playlist.playlist_items.filter(id__in=remove_ids).delete() + + if add_ids: + existing_video_ids = set(playlist.playlist_items.values_list("video_id", flat=True)) + new_video_ids = [vid for vid in add_ids if vid not in existing_video_ids] + current_count = playlist.playlist_items.count() + new_items = [ + PlaylistItem( + playlist=playlist, + video_id=vid, + priority=current_count + idx + 1, + ) + for idx, vid in enumerate(new_video_ids) + ] + if new_items: + PlaylistItem.objects.bulk_create(new_items) + + playlist.total_time = playlist.calculate_total_time() + playlist.save(update_fields=["total_time"]) + + serializer = AdminVideoPlaylistItemSerializer( + playlist.playlist_items.select_related("video").order_by("priority", "id"), + many=True, + context={"request": request}, + ) + return Response(serializer.data, status=status.HTTP_200_OK) +