From ceb294566bb761b544c7f073c48f459f68cc8aa0 Mon Sep 17 00:00:00 2001 From: mohsentaba Date: Mon, 8 Jun 2026 14:50:02 +0330 Subject: [PATCH] podcast views for admin panel added --- .../migrations/0002_podcast_collections.py | 18 +++ apps/podcast/models.py | 2 +- apps/podcast/serializers_admin.py | 130 ++++++++++++++++++ apps/podcast/urls.py | 12 +- apps/podcast/views_admin.py | 73 ++++++++++ 5 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 apps/podcast/migrations/0002_podcast_collections.py create mode 100644 apps/podcast/serializers_admin.py create mode 100644 apps/podcast/views_admin.py diff --git a/apps/podcast/migrations/0002_podcast_collections.py b/apps/podcast/migrations/0002_podcast_collections.py new file mode 100644 index 0000000..86d1b79 --- /dev/null +++ b/apps/podcast/migrations/0002_podcast_collections.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.12 on 2026-06-08 14:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('podcast', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='podcast', + name='collections', + field=models.ManyToManyField(blank=True, related_name='podcasts', to='podcast.podcastcollection', verbose_name='collections'), + ), + ] diff --git a/apps/podcast/models.py b/apps/podcast/models.py index de16e7f..c5d18c1 100644 --- a/apps/podcast/models.py +++ b/apps/podcast/models.py @@ -86,7 +86,7 @@ class Podcast(models.Model): description = models.TextField(null=True) categories = models.ManyToManyField(PodcastCategory, related_name='podcasts', verbose_name=_('categories'), blank=True) - + collections = models.ManyToManyField(PodcastCollection, related_name='podcasts', verbose_name=_('collections'), blank=True) audio_file = models.FileField(upload_to='podcast/audio/', null=True, blank=True) audio_time = models.TimeField() diff --git a/apps/podcast/serializers_admin.py b/apps/podcast/serializers_admin.py new file mode 100644 index 0000000..f10ea46 --- /dev/null +++ b/apps/podcast/serializers_admin.py @@ -0,0 +1,130 @@ +from rest_framework import serializers + +from .models import Podcast , PodcastCategory , PodcastCollection + + +class AbsoluteImageField(serializers.ImageField): + def to_representation(self, value): + if not value: + return None + request = self.context.get("request") + url = value.url if hasattr(value, "url") else str(value) + if request: + return request.build_absolute_uri(url) + return url + + +class AbsoluteFileField(serializers.FileField): + def to_representation(self, value): + if not value: + return None + request = self.context.get("request") + url = value.url if hasattr(value, "url") else str(value) + if request: + return request.build_absolute_uri(url) + return url + + +class AdminPodcastCategorySerializer(serializers.ModelSerializer): + class Meta: + model = PodcastCategory + fields = ["id", "title", "slug", "status", "created_at", "updated_at"] + + +class AdminPodcastCollectionSerializer(serializers.ModelSerializer): + class Meta: + model = PodcastCollection + fields = ["id", "title", "slug", "summary", "display_position", "status", "order", "pin_top"] + + +class AdminPodcastListSerializer(serializers.ModelSerializer): + thumbnail = AbsoluteImageField(required=False, allow_null=True) + audio_file = AbsoluteFileField(required=False, allow_null=True) + categories_detail = AdminPodcastCategorySerializer(source="categories", many=True, read_only=True) + collections_detail = AdminPodcastCollectionSerializer(source="collections", many=True, read_only=True) + + class Meta: + model = Podcast + fields = [ + "id", + "title", + "slug", + "thumbnail", + "status", + "view_count", + "download_count", + "created_at", + "updated_at", + "categories_detail", + "collections_detail", + "audio_file" + ] + read_only_fields = ["id", "view_count", "download_count", "created_at", "updated_at"] + +class AdminPodcastDetailSerializer(serializers.ModelSerializer): + thumbnail = AbsoluteImageField(required=False, allow_null=True) + audio_file = AbsoluteFileField(required=False, allow_null=True) + audio_time = serializers.TimeField(required=False) + categories = serializers.PrimaryKeyRelatedField(queryset=PodcastCategory.objects.all(), many=True, required=False) + collections = serializers.PrimaryKeyRelatedField(queryset=PodcastCollection.objects.all(), many=True, required=False) + categories_detail = AdminPodcastCategorySerializer(source="categories", many=True, read_only=True) + collections_detail = AdminPodcastCollectionSerializer(source="collections", many=True, read_only=True) + remove_thumbnail = serializers.BooleanField(write_only=True, required=False, default=False) + remove_audio_file = serializers.BooleanField(write_only=True, required=False, default=False) + + class Meta: + model = Podcast + fields = [ + "id", + "title", + "slug", + "thumbnail", + "description", + "audio_file", + "audio_time", + "status", + "view_count", + "download_count", + "created_at", + "updated_at", + "categories", + "collections", + "categories_detail", + "collections_detail", + "remove_thumbnail", + "remove_audio_file", + ] + read_only_fields = ["id", "view_count", "download_count", "created_at", "updated_at"] + + def create(self, validated_data): + validated_data.pop("remove_thumbnail", False) + validated_data.pop("remove_audio_file", False) + categories = validated_data.pop("categories", []) + collections = validated_data.pop("collections", []) + podcast = super().create(validated_data) + if categories: + podcast.categories.set(categories) + if collections: + podcast.collections.set(collections) + return podcast + + def update(self, instance, validated_data): + remove_thumbnail = validated_data.pop("remove_thumbnail", False) + remove_audio_file = validated_data.pop("remove_audio_file", False) + categories = validated_data.pop("categories", None) + collections = validated_data.pop("collections", None) + + if remove_thumbnail and instance.thumbnail: + instance.thumbnail.delete(save=False) + instance.thumbnail = None + + if remove_audio_file and instance.audio_file: + instance.audio_file.delete(save=False) + instance.audio_file = None + + podcast = super().update(instance, validated_data) + if categories is not None: + podcast.categories.set(categories) + if collections is not None: + podcast.collections.set(collections) + return podcast diff --git a/apps/podcast/urls.py b/apps/podcast/urls.py index 6a45e13..358ae72 100755 --- a/apps/podcast/urls.py +++ b/apps/podcast/urls.py @@ -1,9 +1,17 @@ -from django.urls import path, re_path +from django.urls import path, re_path , include +from rest_framework.routers import SimpleRouter from .views import * +from .views_admin import * + +admin_router = SimpleRouter() +admin_router.register(r'podcasts', AdminPodcastViewSet, basename='admin-podcast') +admin_router.register(r'categories', AdminPodcastCategoryViewSet, basename='admin-podcast-category') +admin_router.register(r'collections', AdminPodcastCollectionViewSet, basename='admin-podcast-collection') app_name = 'podcast' urlpatterns = [ + path('admin/', include(admin_router.urls)), path('categories/', PodcastCategoryListAPIView.as_view(), name='category-list'), path('pinned-collections/', PinnedPodcastCollectionListView.as_view(), name='pinned-collection-list'), path('collections/', MiddlePodcastCollectionListView.as_view(), name='collection-list'), @@ -14,4 +22,6 @@ urlpatterns = [ # User playlist endpoints path('user-playlist/', UserPlaylistCreateAPIView.as_view(), name='user-playlist-create'), path('user-playlist/list/', UserPlaylistListAPIView.as_view(), name='user-playlist-list'), + + ] \ No newline at end of file diff --git a/apps/podcast/views_admin.py b/apps/podcast/views_admin.py new file mode 100644 index 0000000..7955435 --- /dev/null +++ b/apps/podcast/views_admin.py @@ -0,0 +1,73 @@ +from django.db.models import Q +from rest_framework.authentication import TokenAuthentication +from rest_framework.parsers import FormParser, JSONParser, MultiPartParser +from rest_framework.permissions import IsAuthenticated +from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet + +from apps.account.permissions import IsSuperAdmin +from utils.pagination import StandardResultsSetPagination + +from .models import Podcast, PodcastCollection, PodcastCategory +from .serializers_admin import ( + AdminPodcastCategorySerializer, + AdminPodcastCollectionSerializer, + AdminPodcastDetailSerializer, + AdminPodcastListSerializer, +) + + +class AdminPodcastCategoryViewSet(ReadOnlyModelViewSet): + serializer_class = AdminPodcastCategorySerializer + permission_classes = [IsAuthenticated, IsSuperAdmin] + authentication_classes = [TokenAuthentication] + pagination_class = None + + def get_queryset(self): + return PodcastCategory.objects.all().order_by("title") + + +class AdminPodcastCollectionViewSet(ReadOnlyModelViewSet): + serializer_class = AdminPodcastCollectionSerializer + permission_classes = [IsAuthenticated, IsSuperAdmin] + authentication_classes = [TokenAuthentication] + pagination_class = None + + def get_queryset(self): + return PodcastCollection.objects.all().order_by("display_position", "order", "title") + + +class AdminPodcastViewSet(ModelViewSet): + permission_classes = [IsAuthenticated, IsSuperAdmin] + authentication_classes = [TokenAuthentication] + pagination_class = StandardResultsSetPagination + parser_classes = (MultiPartParser, FormParser, JSONParser) + + def get_serializer_class(self): + if self.action == "list": + return AdminPodcastListSerializer + return AdminPodcastDetailSerializer + + def get_queryset(self): + queryset = Podcast.objects.all().prefetch_related("categories", "collections") + + search_query = self.request.query_params.get("search") + if search_query: + queryset = queryset.filter( + Q(title__icontains=search_query) + | Q(slug__icontains=search_query) + | Q(description__icontains=search_query) + ) + + status_filter = self.request.query_params.get("status") + if status_filter in {"true", "false"}: + queryset = queryset.filter(status=status_filter == "true") + + category_id = self.request.query_params.get("category") + if category_id and category_id != "all": + queryset = queryset.filter(categories__id=category_id) + + collection_id = self.request.query_params.get("collection") + if collection_id and collection_id != "all": + queryset = queryset.filter(collections__id=collection_id) + + return queryset.distinct().order_by("-created_at")