How can I register a single view (not a viewset) on my router?

Routers work with a ViewSet and aren’t designed for normal views, but that doesn’t mean that you cannot use them with a normal view. Normally they are used with models (and a ModelViewSet), but they can be used without them using the GenericViewSet (if you would normally use a GenericAPIView) and ViewSet (if you would just use an APIView).

For a list view, the request methods are mapped to ViewSet methods like this

  • GET -> list(self, request, format=None)
  • POST– > create(self, request, format=None)

For detail views (with a primary key in the url), the request methods use the following map

  • GET -> retrieve(self, request, pk, format=None)
  • PUT -> update(self, request, pk, format=None)
  • PATCH -> partial_update(self, request, pk, format=None)
  • DELETE -> destroy(self, request, pk, format=None)

So if you want to use any of these request methods with your view on your router, you need to override the correct view method (so list() instead of get()).


Now, specifically in your case you would have normally use an APIView that looked like

class UpdateTimeView(APIView):

    def get(self, request, format=None):
        latest_publish = Publish.objects.latest('created_time')
        latest_meeting = Meeting.objects.latest('created_time')
        latest_training = Training.objects.latest('created_time')
        latest_exhibiting = Exhibiting.objects.latest('created_time')

        return Response({
            "publish_updatetime": latest_publish.created_time,
            "meeting_updatetime": latest_meeting.created_time,
            "training_updatetime": latest_training.created_time,
            "exhibiting_updatetime": latest_exhibiting.created_time,
        })

The comparable ViewSet would be

class UpdateTimeViewSet(ViewSet):

    def list(self, request, format=None):
        latest_publish = Publish.objects.latest('created_time')
        latest_meeting = Meeting.objects.latest('created_time')
        latest_training = Training.objects.latest('created_time')
        latest_exhibiting = Exhibiting.objects.latest('created_time')

        return Response({
            "publish_updatetime": latest_publish.created_time,
            "meeting_updatetime": latest_meeting.created_time,
            "training_updatetime": latest_training.created_time,
            "exhibiting_updatetime": latest_exhibiting.created_time,
        })

Notice the two required changes: APIView -> ViewSet and get -> list. I also updated the name to indicate that it was more than just a normal view (as a ViewSet cannot be initialized the same way), but that’s not required.

So with this new view, you can just register it in the router the same way as any other. You need a base_name here so the url names can be generated (normally this would pull from the queryset).

router.register(r'updatetime', views.UpdateTimeViewSet, base_name="updatetime")

So now the updatetime endpoint will be made available in the API root and you can get the latest times by just calling the endpoint (a simple GET request).

Leave a Comment