Skip to content
Advertisement

url path is matching the wrong view in drf viewsets

Django beginner here,

I have two similar endpoints as shown below, they differ in their url_path of the action decorator and the request parameters requester_id and approval id

The problem

both /workflow_approvals/{requester_id}/ and /workflow_approvals/{approval_id}/ are routing to the requester method view(the first below)

    @action(methods=['GET'], detail=False, serializer_class=WorkflowRequesterActivitySerializer, url_path='(?P<requester_id>[^/.]+)')
    def requester_activities(self, request, requester_id, pk=None):
        try:
            approval = WorkflowApproval.objects.filter(requester=requester_id).first()
            if approval is None:
                return Response({'success': False, 'errors': 'No workflow for specified requester'}, status=status.HTTP_404_NOT_FOUND)
            activities = WorkflowActivity.objects.filter(
                workflowapproval=approval
            )
            serializer = self.get_serializer(activities, many=True)
            return Response({'success': True, 'data': {'total_count': activities.count(), 'activities': serializer.data}}, status=status.HTTP_200_OK)
        except Exception as e:
            return Response({'success': False, 'errors': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    @action(methods=['GET'], detail=False, serializer_class=WorkflowActivitySerializer, url_path='(?P<approval_id>w+)',)
    def approval_activities(self, request, approval_id, pk=None):
        try:
            approval = WorkflowApproval.objects.filter(id=approval_id).first()
            if approval is None:
                return Response({'success': False, 'errors': 'Workflow Approval does not exist'},
                                status=status.HTTP_404_NOT_FOUND)
            activities = WorkflowActivity.objects.filter(
                workflowapproval=approval)
            serializer = self.get_serializer(activities, many=True)
            return Response({'success': True, 'data': {'total_count': activities.count(), 'activities': serializer.data}}, status=status.HTTP_200_OK)
        except Exception as e:
            return Response({'success': False, 'errors': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

my urls.py file looks like this

from django.urls import include, path
from rest_framework.routers import DefaultRouter


from .views import WorkflowApprovalsViewset


app_name = "workflow_approvals"

router = DefaultRouter()

router.register("", WorkflowApprovalsViewset)

urlpatterns = [
    path("", include(router.urls)),
]

Advertisement

Answer

Django doesn’t have a way of differentiating /workflow_approvals/{requester_id}/ and /workflow_approvals/{approval_id}/, since the regex pattern for (?P<requester_id>[^/.]+) matches both. So when you make the request with either a requester_id or approval_id, it’ll always end up routing to the former.

You will need instead differentiate the url_paths somehow so they don’t collide. For instance, including an extra string in the path, like matching url_path='requester/(?P<requester_id>[^/.]+)') and url_path='approval/(?P<approval_id>w+)'). (You may want to check out the Routing for extra actions example in the docs.)

But possibly better yet, since it seems you’re trying to filter your list results based on parameters, I’d recommend checking out the Filtering options in Django Rest Framework. You could do something like:

class WorkflowApprovalsViewset(...):
    filterset_fields = ("id", "requester_id",)
    filter_backends = [django_filters.rest_framework.DjangoFilterBackend]

and then be able to query via the standard list route, with query params like ?id=123 or ?requester_id=42.

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement