views.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. from rest_framework import viewsets, status, permissions
  2. from rest_framework.decorators import action
  3. from rest_framework.response import Response
  4. from rest_framework.exceptions import MethodNotAllowed
  5. from .models import Group, Membership, Post, Comment, PostLike # <<<< 确保 PostLike 已导入
  6. from .serializers import (
  7. GroupSerializer,
  8. # MembershipSerializer,
  9. PostSerializer,
  10. CommentSerializer
  11. # PostLikeSerializer, # 目前点赞/取消点赞的action直接返回PostSerializer的数据
  12. )
  13. from .permissions import IsAuthorOrReadOnly
  14. class GroupViewSet(viewsets.ModelViewSet):
  15. queryset = Group.objects.all().order_by('-created_at')
  16. serializer_class = GroupSerializer
  17. def get_permissions(self):
  18. if self.action in ['list', 'retrieve']:
  19. permission_classes = [permissions.AllowAny]
  20. elif self.action in ['list_group_posts', 'create_group_post', 'join_group', 'leave_group', 'my_groups']:
  21. permission_classes = [permissions.IsAuthenticated]
  22. else: # create, update, partial_update, destroy (for groups)
  23. permission_classes = [permissions.IsAuthenticated]
  24. return [permission() for permission in permission_classes]
  25. def perform_create(self, serializer):
  26. group = serializer.save(creator=self.request.user)
  27. Membership.objects.create(user=self.request.user, group=group)
  28. @action(detail=True, methods=['post'], permission_classes=[permissions.IsAuthenticated])
  29. def join_group(self, request, pk=None):
  30. group = self.get_object()
  31. user = request.user
  32. if Membership.objects.filter(user=user, group=group).exists():
  33. return Response({'detail': '您已经是该小组成员。'}, status=status.HTTP_400_BAD_REQUEST)
  34. Membership.objects.create(user=user, group=group)
  35. return Response({'detail': '成功加入小组。'}, status=status.HTTP_200_OK)
  36. @action(detail=True, methods=['post'], permission_classes=[permissions.IsAuthenticated])
  37. def leave_group(self, request, pk=None):
  38. group = self.get_object()
  39. user = request.user
  40. try:
  41. membership = Membership.objects.get(user=user, group=group)
  42. membership.delete()
  43. return Response({'detail': '成功退出小组。'}, status=status.HTTP_200_OK)
  44. except Membership.DoesNotExist:
  45. return Response({'detail': '您不是该小组成员。'}, status=status.HTTP_400_BAD_REQUEST)
  46. @action(detail=False, methods=['get'], permission_classes=[permissions.IsAuthenticated], url_path='my-groups')
  47. def my_groups(self, request):
  48. user = request.user
  49. memberships = Membership.objects.filter(user=user).select_related('group')
  50. groups = [membership.group for membership in memberships]
  51. page = self.paginate_queryset(groups)
  52. if page is not None:
  53. serializer = self.get_serializer(page, many=True, context={'request': request})
  54. return self.get_paginated_response(serializer.data)
  55. serializer = self.get_serializer(groups, many=True, context={'request': request})
  56. return Response(serializer.data)
  57. @action(detail=True, methods=['get'], url_path='posts', url_name='group-posts-list')
  58. def list_group_posts(self, request, pk=None):
  59. group = self.get_object()
  60. posts_queryset = Post.objects.filter(group=group).order_by('-created_at')
  61. page = self.paginate_queryset(posts_queryset)
  62. if page is not None:
  63. serializer = PostSerializer(page, many=True, context={'request': request})
  64. return self.get_paginated_response(serializer.data)
  65. serializer = PostSerializer(posts_queryset, many=True, context={'request': request})
  66. return Response(serializer.data)
  67. @action(detail=True, methods=['post'], url_path='create-post', url_name='group-post-create')
  68. def create_group_post(self, request, pk=None):
  69. group = self.get_object()
  70. serializer = PostSerializer(data=request.data, context={'request': request})
  71. if serializer.is_valid():
  72. serializer.save(author=request.user, group=group)
  73. return Response(serializer.data, status=status.HTTP_201_CREATED)
  74. return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  75. class PostViewSet(viewsets.ModelViewSet):
  76. queryset = Post.objects.all().select_related('author', 'group').order_by('-created_at')
  77. serializer_class = PostSerializer # 主序列化器是 PostSerializer
  78. def get_permissions(self):
  79. if self.action in ['list', 'retrieve']:
  80. permission_classes = [permissions.IsAuthenticated]
  81. elif self.action in ['update', 'partial_update', 'destroy']:
  82. permission_classes = [permissions.IsAuthenticated, IsAuthorOrReadOnly]
  83. elif self.action in ['list_comments', 'create_comment', 'like_post', 'unlike_post']: # <<<< 添加了 like_post, unlike_post
  84. permission_classes = [permissions.IsAuthenticated]
  85. elif self.action == 'create':
  86. permission_classes = [permissions.IsAdminUser]
  87. else:
  88. permission_classes = [permissions.IsAuthenticated]
  89. return [permission() for permission in permission_classes]
  90. def perform_create(self, serializer):
  91. raise MethodNotAllowed(
  92. method=self.request.method,
  93. detail="不推荐通过此接口创建帖子。请通过小组接口 /api/v1/groups/<group_pk>/create-post/ 创建。"
  94. )
  95. @action(detail=True, methods=['get'], url_path='comment-list', url_name='post-comments-list-alt')
  96. def list_comments(self, request, pk=None):
  97. post = self.get_object()
  98. comments_queryset = Comment.objects.filter(post=post).select_related('author').order_by('created_at')
  99. page = self.paginate_queryset(comments_queryset)
  100. if page is not None:
  101. serializer = CommentSerializer(page, many=True, context={'request': request})
  102. return self.get_paginated_response(serializer.data)
  103. serializer = CommentSerializer(comments_queryset, many=True, context={'request': request})
  104. return Response(serializer.data)
  105. @action(detail=True, methods=['post'], url_path='comments', url_name='post-comment-create')
  106. def create_comment(self, request, pk=None):
  107. post = self.get_object()
  108. serializer = CommentSerializer(data=request.data, context={'request': request})
  109. if serializer.is_valid():
  110. serializer.save(author=request.user, post=post)
  111. return Response(serializer.data, status=status.HTTP_201_CREATED)
  112. return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  113. # --- 新增:用于帖子点赞/取消点赞的 Action ---
  114. @action(detail=True, methods=['post'], url_path='like', url_name='post-like')
  115. def like_post(self, request, pk=None):
  116. """
  117. 当前登录用户点赞指定ID的帖子。
  118. POST /api/v1/posts/<pk>/like/
  119. """
  120. post = self.get_object() # 获取pk对应的Post实例
  121. user = request.user
  122. if PostLike.objects.filter(user=user, post=post).exists():
  123. return Response({'detail': '您已经点赞过该帖子了。'}, status=status.HTTP_400_BAD_REQUEST)
  124. PostLike.objects.create(user=user, post=post)
  125. # 返回更新后的帖子信息(包含新的点赞数和is_liked状态)
  126. # get_serializer() 会使用 PostViewSet 的 self.serializer_class (即 PostSerializer)
  127. serializer = self.get_serializer(post, context={'request': request})
  128. return Response(serializer.data, status=status.HTTP_200_OK)
  129. @action(detail=True, methods=['post'], url_path='unlike', url_name='post-unlike')
  130. def unlike_post(self, request, pk=None):
  131. """
  132. 当前登录用户取消点赞指定ID的帖子。
  133. POST /api/v1/posts/<pk>/unlike/
  134. """
  135. post = self.get_object()
  136. user = request.user
  137. try:
  138. like = PostLike.objects.get(user=user, post=post)
  139. like.delete()
  140. # 返回更新后的帖子信息
  141. serializer = self.get_serializer(post, context={'request': request})
  142. return Response(serializer.data, status=status.HTTP_200_OK)
  143. except PostLike.DoesNotExist:
  144. return Response({'detail': '您还没有点赞该帖子。'}, status=status.HTTP_400_BAD_REQUEST)
  145. class CommentViewSet(viewsets.ModelViewSet):
  146. queryset = Comment.objects.all().select_related('author', 'post').order_by('-created_at')
  147. serializer_class = CommentSerializer
  148. def get_permissions(self):
  149. if self.action in ['list', 'retrieve']:
  150. permission_classes = [permissions.IsAuthenticated]
  151. elif self.action in ['update', 'partial_update', 'destroy']:
  152. permission_classes = [permissions.IsAuthenticated, IsAuthorOrReadOnly]
  153. elif self.action == 'create':
  154. permission_classes = [permissions.IsAdminUser]
  155. else:
  156. permission_classes = [permissions.IsAuthenticated]
  157. return [permission() for permission in permission_classes]
  158. def perform_create(self, serializer):
  159. raise MethodNotAllowed(
  160. method=self.request.method,
  161. detail="请通过帖子接口 /api/v1/posts/<post_pk>/comments/ 创建评论。"
  162. )