from rest_framework import viewsets, status, filters from rest_framework.decorators import action from rest_framework.response import Response from rest_framework.pagination import PageNumberPagination from django_filters.rest_framework import DjangoFilterBackend from django.db.models import Q from .models import BlogPost, BlogCategory, BlogAuthor, BlogTag, BlogComment from .serializers import ( BlogPostListSerializer, BlogPostDetailSerializer, BlogCategorySerializer, BlogAuthorSerializer, BlogTagSerializer, BlogCommentSerializer, BlogCommentCreateSerializer ) class BlogPagination(PageNumberPagination): """Custom pagination for blog posts""" page_size = 9 page_size_query_param = 'page_size' max_page_size = 100 class BlogPostViewSet(viewsets.ReadOnlyModelViewSet): """ ViewSet for blog posts Supports filtering by category, tags, author, and search """ queryset = BlogPost.objects.filter(published=True) pagination_class = BlogPagination filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter] filterset_fields = ['category__slug', 'author', 'featured', 'tags__slug'] search_fields = ['title', 'content', 'excerpt'] ordering_fields = ['published_at', 'views_count', 'created_at'] ordering = ['-published_at'] lookup_field = 'slug' def get_serializer_class(self): if self.action == 'retrieve': return BlogPostDetailSerializer return BlogPostListSerializer def get_queryset(self): queryset = super().get_queryset() # Filter by category category_slug = self.request.query_params.get('category', None) if category_slug and category_slug != 'all': queryset = queryset.filter(category__slug=category_slug) # Filter by tag tag_slug = self.request.query_params.get('tag', None) if tag_slug: queryset = queryset.filter(tags__slug=tag_slug) # Filter by author author_id = self.request.query_params.get('author', None) if author_id: queryset = queryset.filter(author_id=author_id) # Search query search = self.request.query_params.get('search', None) if search: queryset = queryset.filter( Q(title__icontains=search) | Q(content__icontains=search) | Q(excerpt__icontains=search) ) return queryset.distinct() def retrieve(self, request, *args, **kwargs): """Override retrieve to increment view count""" instance = self.get_object() instance.increment_views() serializer = self.get_serializer(instance) return Response(serializer.data) @action(detail=False, methods=['get']) def featured(self, request): """Get featured blog posts""" featured_posts = self.get_queryset().filter(featured=True)[:6] serializer = self.get_serializer(featured_posts, many=True) return Response(serializer.data) @action(detail=False, methods=['get']) def latest(self, request): """Get latest blog posts""" limit = int(request.query_params.get('limit', 5)) latest_posts = self.get_queryset()[:limit] serializer = self.get_serializer(latest_posts, many=True) return Response(serializer.data) @action(detail=False, methods=['get']) def popular(self, request): """Get popular blog posts by views""" limit = int(request.query_params.get('limit', 5)) popular_posts = self.get_queryset().order_by('-views_count')[:limit] serializer = self.get_serializer(popular_posts, many=True) return Response(serializer.data) @action(detail=True, methods=['get']) def related(self, request, slug=None): """Get related posts for a specific post""" post = self.get_object() related_posts = self.get_queryset().filter( category=post.category ).exclude(id=post.id)[:4] serializer = self.get_serializer(related_posts, many=True) return Response(serializer.data) class BlogCategoryViewSet(viewsets.ReadOnlyModelViewSet): """ViewSet for blog categories""" queryset = BlogCategory.objects.filter(is_active=True) serializer_class = BlogCategorySerializer lookup_field = 'slug' @action(detail=False, methods=['get']) def with_posts(self, request): """Get categories that have published posts""" categories = self.get_queryset().filter(posts__published=True).distinct() serializer = self.get_serializer(categories, many=True) return Response(serializer.data) class BlogAuthorViewSet(viewsets.ReadOnlyModelViewSet): """ViewSet for blog authors""" queryset = BlogAuthor.objects.filter(is_active=True) serializer_class = BlogAuthorSerializer @action(detail=True, methods=['get']) def posts(self, request, pk=None): """Get all posts by a specific author""" author = self.get_object() posts = BlogPost.objects.filter(author=author, published=True) page = self.paginate_queryset(posts) if page is not None: serializer = BlogPostListSerializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = BlogPostListSerializer(posts, many=True) return Response(serializer.data) class BlogTagViewSet(viewsets.ReadOnlyModelViewSet): """ViewSet for blog tags""" queryset = BlogTag.objects.filter(is_active=True) serializer_class = BlogTagSerializer lookup_field = 'slug' @action(detail=True, methods=['get']) def posts(self, request, slug=None): """Get all posts with a specific tag""" tag = self.get_object() posts = BlogPost.objects.filter(tags=tag, published=True) page = self.paginate_queryset(posts) if page is not None: serializer = BlogPostListSerializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = BlogPostListSerializer(posts, many=True) return Response(serializer.data) class BlogCommentViewSet(viewsets.ModelViewSet): """ViewSet for blog comments""" queryset = BlogComment.objects.filter(is_approved=True) serializer_class = BlogCommentSerializer filter_backends = [DjangoFilterBackend] filterset_fields = ['post'] def get_serializer_class(self): if self.action == 'create': return BlogCommentCreateSerializer return BlogCommentSerializer def create(self, request, *args, **kwargs): """Create a new comment (requires moderation)""" serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) self.perform_create(serializer) return Response( { 'message': 'Comment submitted successfully. It will be visible after moderation.', 'data': serializer.data }, status=status.HTTP_201_CREATED )