[Django]-Django-Rest-Framework: Can I create a viewset with two different methods with the same url_path, but different request methods?

3👍

Since your url_path and detail are the same in both cases, why do you want two separate methods in your views??
Anyway I would recommend this way,

class MobileDeviceViewset(ModelViewSet):
    # your code

    @action(methods=['get', 'post', 'put', 'patch'], url_path='token', detail=True, )
    def my_action(self, request, *args, **kwargs):
        if request.method == 'GET':
            return self.get_token(request, *args, **kwargs)
        else:
            return self.update_token(request, *args, **kwargs)

    def update_token(self, request, *args, **kwargs):
        return Response("update token response--{}".format(request.method))

    def get_token(self, request, *args, **kwargs):
        return Response("update token response--{}".format(request.method))

Then you have to make some changes in your URL config,

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

router = DefaultRouter()
router.register('mysample', MobileDeviceViewset, base_name='mobile-device')
actions = {
    "get": "my_action",
    "post": "my_action",
    "put": "my_action",
    "patch": "my_action"
}
urlpatterns = [
                  path('mysample/<pk>/token/', MobileDeviceViewset.as_view(actions=actions))

              ] + router.urls

Hence, your URL will some something like, ..../mysample/3/token/

NOTE
This solution tested under Python 3.6, Django==2.1 and DRF==3.8.2


UPDATE

Why Method Not Allowed error?

When a request comes to Django, it searches for the patterns in the URL configs and sends the request to the corresponding view if a match occurs.
In your case, you’ve defined two views (yes..it’s actions though) with the same URL (as below,).

actions = {
    "post": "update_token",
    "put": "update_token",
    "patch": "update_token"
}

urlpatterns = [
                  path('mysample/<pk>/token/', MobileDeviceViewset.as_view(actions={"get": "get_token"})),
                  path('mysample/<pk>/token/', MobileDeviceViewset.as_view(actions=actions))

              ] + router.urls

In this case, a request comes (let it be a HTTP POST) and the URL dispatcher redirects to the first view which satisfies the URL path. So, the POST request goes into the get_token method, but, it’s only allowed for GET method


What is the possible solution?

Method-1:
As I described in the top, use a common action and distingush the HTTP METHODS and call appropriate methods
Method-2:
Use different URL path for both actions, as

actions = {
    "post": "my_action",
    "put": "my_action",
    "patch": "my_action"
}
urlpatterns = [
                  path('mysample/<pk>/get-token/', MobileDeviceViewset.as_view(actions={"get": "get_token"})),
                  path('mysample/<pk>/update-token/', MobileDeviceViewset.as_view(actions=actions))

              ] + router.urls
👤JPG

3👍

you can do this via:

class MobileDeviceViewset(ModelViewSet):    

    @action(
        methods=['get'],
        url_path='token',
        url_name='token',
        detail=True,
    )
    def get_token(self, request, *args, **kwargs) -> Response:
        ...

    @get_token.mapping.post
    @get_token.mapping.put
    @get_token.mapping.patch
    def update_token(self, request, *args, **kwargs) -> Response:
        ...

https://www.django-rest-framework.org/api-guide/viewsets/#routing-additional-http-methods-for-extra-actions

Leave a comment