from rest_framework import viewsets, status, permissions from rest_framework.decorators import action from rest_framework.response import Response from rest_framework.exceptions import MethodNotAllowed from .models import Group, Membership, Post, Comment, PostLike # <<<< 确保 PostLike 已导入 from .serializers import ( GroupSerializer, # MembershipSerializer, PostSerializer, CommentSerializer # PostLikeSerializer, # 目前点赞/取消点赞的action直接返回PostSerializer的数据 ) from .permissions import IsAuthorOrReadOnly class GroupViewSet(viewsets.ModelViewSet): queryset = Group.objects.all().order_by('-created_at') serializer_class = GroupSerializer def get_permissions(self): if self.action in ['list', 'retrieve']: permission_classes = [permissions.AllowAny] elif self.action in ['list_group_posts', 'create_group_post', 'join_group', 'leave_group', 'my_groups']: permission_classes = [permissions.IsAuthenticated] else: # create, update, partial_update, destroy (for groups) permission_classes = [permissions.IsAuthenticated] return [permission() for permission in permission_classes] def perform_create(self, serializer): group = serializer.save(creator=self.request.user) Membership.objects.create(user=self.request.user, group=group) @action(detail=True, methods=['post'], permission_classes=[permissions.IsAuthenticated]) def join_group(self, request, pk=None): group = self.get_object() user = request.user if Membership.objects.filter(user=user, group=group).exists(): return Response({'detail': '您已经是该小组成员。'}, status=status.HTTP_400_BAD_REQUEST) Membership.objects.create(user=user, group=group) return Response({'detail': '成功加入小组。'}, status=status.HTTP_200_OK) @action(detail=True, methods=['post'], permission_classes=[permissions.IsAuthenticated]) def leave_group(self, request, pk=None): group = self.get_object() user = request.user try: membership = Membership.objects.get(user=user, group=group) membership.delete() return Response({'detail': '成功退出小组。'}, status=status.HTTP_200_OK) except Membership.DoesNotExist: return Response({'detail': '您不是该小组成员。'}, status=status.HTTP_400_BAD_REQUEST) @action(detail=False, methods=['get'], permission_classes=[permissions.IsAuthenticated], url_path='my-groups') def my_groups(self, request): user = request.user memberships = Membership.objects.filter(user=user).select_related('group') groups = [membership.group for membership in memberships] page = self.paginate_queryset(groups) if page is not None: serializer = self.get_serializer(page, many=True, context={'request': request}) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(groups, many=True, context={'request': request}) return Response(serializer.data) @action(detail=True, methods=['get'], url_path='posts', url_name='group-posts-list') def list_group_posts(self, request, pk=None): group = self.get_object() posts_queryset = Post.objects.filter(group=group).order_by('-created_at') page = self.paginate_queryset(posts_queryset) if page is not None: serializer = PostSerializer(page, many=True, context={'request': request}) return self.get_paginated_response(serializer.data) serializer = PostSerializer(posts_queryset, many=True, context={'request': request}) return Response(serializer.data) @action(detail=True, methods=['post'], url_path='create-post', url_name='group-post-create') def create_group_post(self, request, pk=None): group = self.get_object() serializer = PostSerializer(data=request.data, context={'request': request}) if serializer.is_valid(): serializer.save(author=request.user, group=group) return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) class PostViewSet(viewsets.ModelViewSet): queryset = Post.objects.all().select_related('author', 'group').order_by('-created_at') serializer_class = PostSerializer # 主序列化器是 PostSerializer def get_permissions(self): if self.action in ['list', 'retrieve']: permission_classes = [permissions.IsAuthenticated] elif self.action in ['update', 'partial_update', 'destroy']: permission_classes = [permissions.IsAuthenticated, IsAuthorOrReadOnly] elif self.action in ['list_comments', 'create_comment', 'like_post', 'unlike_post']: # <<<< 添加了 like_post, unlike_post permission_classes = [permissions.IsAuthenticated] elif self.action == 'create': permission_classes = [permissions.IsAdminUser] else: permission_classes = [permissions.IsAuthenticated] return [permission() for permission in permission_classes] def perform_create(self, serializer): raise MethodNotAllowed( method=self.request.method, detail="不推荐通过此接口创建帖子。请通过小组接口 /api/v1/groups//create-post/ 创建。" ) @action(detail=True, methods=['get'], url_path='comment-list', url_name='post-comments-list-alt') def list_comments(self, request, pk=None): post = self.get_object() comments_queryset = Comment.objects.filter(post=post).select_related('author').order_by('created_at') page = self.paginate_queryset(comments_queryset) if page is not None: serializer = CommentSerializer(page, many=True, context={'request': request}) return self.get_paginated_response(serializer.data) serializer = CommentSerializer(comments_queryset, many=True, context={'request': request}) return Response(serializer.data) @action(detail=True, methods=['post'], url_path='comments', url_name='post-comment-create') def create_comment(self, request, pk=None): post = self.get_object() serializer = CommentSerializer(data=request.data, context={'request': request}) if serializer.is_valid(): serializer.save(author=request.user, post=post) return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) # --- 新增:用于帖子点赞/取消点赞的 Action --- @action(detail=True, methods=['post'], url_path='like', url_name='post-like') def like_post(self, request, pk=None): """ 当前登录用户点赞指定ID的帖子。 POST /api/v1/posts//like/ """ post = self.get_object() # 获取pk对应的Post实例 user = request.user if PostLike.objects.filter(user=user, post=post).exists(): return Response({'detail': '您已经点赞过该帖子了。'}, status=status.HTTP_400_BAD_REQUEST) PostLike.objects.create(user=user, post=post) # 返回更新后的帖子信息(包含新的点赞数和is_liked状态) # get_serializer() 会使用 PostViewSet 的 self.serializer_class (即 PostSerializer) serializer = self.get_serializer(post, context={'request': request}) return Response(serializer.data, status=status.HTTP_200_OK) @action(detail=True, methods=['post'], url_path='unlike', url_name='post-unlike') def unlike_post(self, request, pk=None): """ 当前登录用户取消点赞指定ID的帖子。 POST /api/v1/posts//unlike/ """ post = self.get_object() user = request.user try: like = PostLike.objects.get(user=user, post=post) like.delete() # 返回更新后的帖子信息 serializer = self.get_serializer(post, context={'request': request}) return Response(serializer.data, status=status.HTTP_200_OK) except PostLike.DoesNotExist: return Response({'detail': '您还没有点赞该帖子。'}, status=status.HTTP_400_BAD_REQUEST) class CommentViewSet(viewsets.ModelViewSet): queryset = Comment.objects.all().select_related('author', 'post').order_by('-created_at') serializer_class = CommentSerializer def get_permissions(self): if self.action in ['list', 'retrieve']: permission_classes = [permissions.IsAuthenticated] elif self.action in ['update', 'partial_update', 'destroy']: permission_classes = [permissions.IsAuthenticated, IsAuthorOrReadOnly] elif self.action == 'create': permission_classes = [permissions.IsAdminUser] else: permission_classes = [permissions.IsAuthenticated] return [permission() for permission in permission_classes] def perform_create(self, serializer): raise MethodNotAllowed( method=self.request.method, detail="请通过帖子接口 /api/v1/posts//comments/ 创建评论。" )