190 lines
7.0 KiB
Python
190 lines
7.0 KiB
Python
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
|
|
)
|