From bec7105bde7de8580263e919cdc0beab150ad255 Mon Sep 17 00:00:00 2001
From: sks444 <krishnasingh.ss30@gmail.com>
Date: Mon, 3 Aug 2020 17:11:50 +0530
Subject: [PATCH] Use drf ViewSet and routers instead of api view

---
 hypha/apply/api/v1/mixin.py       |  10 +++
 hypha/apply/api/v1/serializers.py |   8 +-
 hypha/apply/api/v1/urls.py        |  42 ++++-----
 hypha/apply/api/v1/views.py       | 139 +++++++++++++++---------------
 4 files changed, 99 insertions(+), 100 deletions(-)
 create mode 100644 hypha/apply/api/v1/mixin.py

diff --git a/hypha/apply/api/v1/mixin.py b/hypha/apply/api/v1/mixin.py
new file mode 100644
index 000000000..67d8fe843
--- /dev/null
+++ b/hypha/apply/api/v1/mixin.py
@@ -0,0 +1,10 @@
+from django.shortcuts import get_object_or_404
+
+from hypha.apply.funds.models import ApplicationSubmission
+
+
+class SubmissionNextedMixin:
+    def get_submission_object(self):
+        return get_object_or_404(
+            ApplicationSubmission, id=self.kwargs['submission_pk']
+        )
diff --git a/hypha/apply/api/v1/serializers.py b/hypha/apply/api/v1/serializers.py
index 8778d56b5..60a086c2d 100644
--- a/hypha/apply/api/v1/serializers.py
+++ b/hypha/apply/api/v1/serializers.py
@@ -110,7 +110,7 @@ class TimestampField(serializers.Field):
 
 
 class SubmissionListSerializer(serializers.ModelSerializer):
-    url = serializers.HyperlinkedIdentityField(view_name='api:v1:submissions:detail')
+    url = serializers.HyperlinkedIdentityField(view_name='api:v1:submissions-detail')
     round = serializers.SerializerMethodField()
     last_update = TimestampField()
 
@@ -199,14 +199,14 @@ class RoundLabSerializer(serializers.ModelSerializer):
 class CommentSerializer(serializers.ModelSerializer):
     user = serializers.StringRelatedField()
     message = serializers.SerializerMethodField()
-    edit_url = serializers.HyperlinkedIdentityField(view_name='api:v1:comments:edit')
+    edit_url = serializers.HyperlinkedIdentityField(view_name='api:v1:comments-edit')
     editable = serializers.SerializerMethodField()
     timestamp = TimestampField(read_only=True)
     edited = TimestampField(read_only=True)
 
     class Meta:
         model = Activity
-        fields = ('id', 'timestamp', 'user', 'source', 'message', 'visibility', 'edited', 'edit_url', 'editable')
+        fields = ('id', 'timestamp', 'user', 'message', 'visibility', 'edited', 'edit_url', 'editable')
 
     def get_message(self, obj):
         return bleach_value(markdown(obj.message))
@@ -217,7 +217,7 @@ class CommentSerializer(serializers.ModelSerializer):
 
 class CommentCreateSerializer(serializers.ModelSerializer):
     user = serializers.StringRelatedField()
-    edit_url = serializers.HyperlinkedIdentityField(view_name='api:v1:comments:edit')
+    edit_url = serializers.HyperlinkedIdentityField(view_name='api:v1:comments-edit')
     editable = serializers.SerializerMethodField()
     timestamp = TimestampField(read_only=True)
     edited = TimestampField(read_only=True)
diff --git a/hypha/apply/api/v1/urls.py b/hypha/apply/api/v1/urls.py
index 102a35920..55fbd1ebb 100644
--- a/hypha/apply/api/v1/urls.py
+++ b/hypha/apply/api/v1/urls.py
@@ -1,31 +1,23 @@
-from django.urls import include, path
+from rest_framework_nested import routers
 
 from .views import (
-    CommentEdit,
-    CommentList,
-    CommentListCreate,
-    RoundLabDetail,
-    RoundLabList,
-    SubmissionAction,
-    SubmissionDetail,
-    SubmissionList,
+    SubmissionViewSet,
+    SubmissionActionViewSet,
+    SubmissionCommentViewSet,
+    CommentViewSet,
+    RoundViewSet,
 )
 
 app_name = 'v1'
 
-urlpatterns = [
-    path('submissions/', include(([
-        path('', SubmissionList.as_view(), name='list'),
-        path('<int:pk>/', SubmissionDetail.as_view(), name='detail'),
-        path('<int:pk>/actions/', SubmissionAction.as_view(), name='actions'),
-        path('<int:pk>/comments/', CommentListCreate.as_view(), name='comments'),
-    ], 'submissions'))),
-    path('rounds/', include(([
-        path('', RoundLabList.as_view(), name='list'),
-        path('<int:pk>/', RoundLabDetail.as_view(), name='detail'),
-    ], 'rounds'))),
-    path('comments/', include(([
-        path('', CommentList.as_view(), name='list'),
-        path('<int:pk>/edit/', CommentEdit.as_view(), name='edit'),
-    ], 'comments')))
-]
+
+router = routers.SimpleRouter()
+router.register(r'submissions', SubmissionViewSet, base_name='submissions')
+router.register(r'comments', CommentViewSet, base_name='comments')
+router.register(r'rounds', RoundViewSet, base_name='rounds')
+
+submission_router = routers.NestedSimpleRouter(router, r'submissions', lookup='submission')
+submission_router.register(r'actions', SubmissionActionViewSet, base_name='submission-actions')
+submission_router.register(r'comments', SubmissionCommentViewSet, base_name='submission-comments')
+
+urlpatterns = router.urls + submission_router.urls
diff --git a/hypha/apply/api/v1/views.py b/hypha/apply/api/v1/views.py
index 3d483dfd5..93e27dcc7 100644
--- a/hypha/apply/api/v1/views.py
+++ b/hypha/apply/api/v1/views.py
@@ -3,9 +3,10 @@ from django.db import transaction
 from django.db.models import Prefetch, Q
 from django.utils import timezone
 from django_filters import rest_framework as filters
-from rest_framework import generics, mixins, permissions
+from rest_framework import mixins, permissions, viewsets
 from rest_framework.exceptions import NotFound, PermissionDenied, ValidationError
 from rest_framework.response import Response
+from rest_framework.decorators import action
 from rest_framework_api_key.permissions import HasAPIKey
 from wagtail.core.models import Page
 
@@ -33,6 +34,7 @@ from .serializers import (
     SubmissionDetailSerializer,
     SubmissionListSerializer,
 )
+from .mixin import SubmissionNextedMixin
 
 
 class RoundLabFilter(filters.ModelChoiceFilter):
@@ -67,12 +69,7 @@ class SubmissionsFilter(filters.FilterSet):
             return qs.inactive()
 
 
-class SubmissionList(generics.ListAPIView):
-    """
-    List all the submissions.
-    """
-    queryset = ApplicationSubmission.objects.current().with_latest_update()
-    serializer_class = SubmissionListSerializer
+class SubmissionViewSet(viewsets.ReadOnlyModelViewSet):
     permission_classes = (
         HasAPIKey | permissions.IsAuthenticated, HasAPIKey | IsApplyStaffUser,
     )
@@ -80,33 +77,42 @@ class SubmissionList(generics.ListAPIView):
     filter_class = SubmissionsFilter
     pagination_class = StandardResultsSetPagination
 
+    def get_serializer_class(self):
+        if self.action == 'list':
+            return SubmissionListSerializer
+        return SubmissionDetailSerializer
 
-class SubmissionDetail(generics.RetrieveAPIView):
-    """
-    Get details about a submission by it's id.
-    """
-    queryset = ApplicationSubmission.objects.all().prefetch_related(
-        Prefetch('reviews', Review.objects.submitted()),
-    )
-    serializer_class = SubmissionDetailSerializer
-    permission_classes = (
-        permissions.IsAuthenticated, IsApplyStaffUser,
-    )
-
+    def get_queryset(self):
+        if self.action == 'list':
+            return ApplicationSubmission.objects.current().with_latest_update()
+        return ApplicationSubmission.objects.all().prefetch_related(
+            Prefetch('reviews', Review.objects.submitted()),
+        )
 
-class SubmissionAction(generics.RetrieveAPIView):
-    """
-    List all the actions that can be taken on a submission.
 
-    E.g. All the states this submission can be transistion to.
-    """
-    queryset = ApplicationSubmission.objects.all()
+class SubmissionActionViewSet(
+    SubmissionNextedMixin,
+    viewsets.GenericViewSet
+):
     serializer_class = SubmissionActionSerializer
     permission_classes = (
         permissions.IsAuthenticated, IsApplyStaffUser,
     )
 
-    def post(self, request, *args, **kwargs):
+    def get_object(self):
+        return self.get_submission_object()
+
+    def list(self, request, *args, **kwargs):
+        """
+        List all the actions that can be taken on a submission.
+
+        E.g. All the states this submission can be transistion to.
+        """
+        obj = self.get_object()
+        ser = self.get_serializer(obj)
+        return Response(ser.data)
+
+    def create(self, request, *args, **kwargs):
         """
         Transistion a submission from one state to other.
 
@@ -146,24 +152,11 @@ class SubmissionAction(generics.RetrieveAPIView):
         })
 
 
-class RoundLabDetail(generics.RetrieveAPIView):
-    """
-    Get detail about a round or a lab.
-    """
-    queryset = RoundsAndLabs.objects.all()
-    serializer_class = RoundLabDetailSerializer
-    permission_classes = (
-        permissions.IsAuthenticated, IsApplyStaffUser,
-    )
-
-    def get_object(self):
-        return super().get_object().specific
-
-
-class RoundLabList(generics.ListAPIView):
-    """
-    List all the rounds and labs current user has access to.
-    """
+class RoundViewSet(
+    mixins.RetrieveModelMixin,
+    mixins.ListModelMixin,
+    viewsets.GenericViewSet
+):
     queryset = RoundsAndLabs.objects.specific()
     serializer_class = RoundLabSerializer
     permission_classes = (
@@ -171,6 +164,11 @@ class RoundLabList(generics.ListAPIView):
     )
     pagination_class = StandardResultsSetPagination
 
+    def get_serializer_class(self):
+        if self.action == 'list':
+            return RoundLabSerializer
+        return RoundLabDetailSerializer
+
 
 class NewerThanFilter(filters.ModelChoiceFilter):
     def filter(self, qs, value):
@@ -195,24 +193,12 @@ class AllCommentFilter(CommentFilter):
         fields = CommentFilter.Meta.fields + ['source_object_id']
 
 
-class CommentList(generics.ListAPIView):
-    """
-    List all the comments for a user.
-    """
-    queryset = Activity.comments.all()
-    serializer_class = CommentSerializer
-    permission_classes = (
-        permissions.IsAuthenticated, IsApplyStaffUser,
-    )
-    filter_backends = (filters.DjangoFilterBackend,)
-    filter_class = AllCommentFilter
-    pagination_class = StandardResultsSetPagination
-
-    def get_queryset(self):
-        return super().get_queryset().visible_to(self.request.user)
-
-
-class CommentListCreate(generics.ListCreateAPIView):
+class SubmissionCommentViewSet(
+    SubmissionNextedMixin,
+    mixins.ListModelMixin,
+    mixins.CreateModelMixin,
+    viewsets.GenericViewSet
+):
     """
     List all the comments on a submission.
     """
@@ -227,7 +213,7 @@ class CommentListCreate(generics.ListCreateAPIView):
 
     def get_queryset(self):
         return super().get_queryset().filter(
-            submission=self.kwargs['pk']
+            submission=self.get_submission_object()
         ).visible_to(self.request.user)
 
     def perform_create(self, serializer):
@@ -238,7 +224,7 @@ class CommentListCreate(generics.ListCreateAPIView):
             timestamp=timezone.now(),
             type=COMMENT,
             user=self.request.user,
-            source=ApplicationSubmission.objects.get(pk=self.kwargs['pk'])
+            source=self.get_submission_object()
         )
         messenger(
             MESSAGES.COMMENT,
@@ -249,10 +235,9 @@ class CommentListCreate(generics.ListCreateAPIView):
         )
 
 
-class CommentEdit(
-        mixins.RetrieveModelMixin,
-        mixins.CreateModelMixin,
-        generics.GenericAPIView,
+class CommentViewSet(
+        mixins.ListModelMixin,
+        viewsets.GenericViewSet,
 ):
     """
     Edit a comment.
@@ -263,11 +248,20 @@ class CommentEdit(
         permissions.IsAuthenticated, IsAuthor
     )
 
-    def post(self, request, *args, **kwargs):
-        return self.edit(request, *args, **kwargs)
+    def get_serializer_class(self):
+        if self.action == 'list':
+            return CommentSerializer
+        return CommentEditSerializer
 
-    @transaction.atomic
+    def get_queryset(self):
+        return super().get_queryset().visible_to(self.request.user)
+
+    @action(detail=True, methods=['post'])
     def edit(self, request, *args, **kwargs):
+        return self.edit_comment(request, *args, **kwargs)
+
+    @transaction.atomic
+    def edit_comment(self, request, *args, **kwargs):
         comment_to_edit = self.get_object()
         comment_to_update = self.get_object()
 
@@ -285,3 +279,6 @@ class CommentEdit(
             return Response(serializer.data)
 
         return Response(self.get_serializer(comment_to_update).data)
+
+    def perform_create(self, serializer):
+        serializer.save()
-- 
GitLab