Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to allow POST for update action in django rest framework

Tags:

We're building an API with Django Rest Framework, and our clients want it to have this endpoint:

Method: POST
URL: api/v1/application/<id>

It's basically an update endpoint but with POST instead of PUT.

I already have a viewset for this model, and we're using the create, list, and retrieve actions.

So, one thing I can do is to allow POST for update actions in Django Rest Framework, but I haven't found how to do that. Also, I can define a custom action like this:

@action(methods=['post'], detail=True)
def update_status(self, request, pk=None):
    # some code

The problem is this routes to application/<id>/update_status, I can change the route by passing the url_path parameter, but if it's None or empty it just defaults to update_status again.

I can also just define this endpoint in a different view and route manually, but that's a worse solution in my opinion, it would be nice to have it in the viewset I already have.

Thanks.

like image 436
Daniel Avatar asked Dec 31 '18 17:12

Daniel


2 Answers

Django REST framework allows binding viewsets to URLs explicitly. You can create a set of views from your viewset and bind the URLs without a router

views.py

from restframework import viewsets, mixins

class ApplicationViewSet(mixins.ListModelMixin,
                         mixins.RetrieveModelMixin,
                         mixins.CreateModelMixin,
                         viewsets.GenericViewSet):
    # Your viewset settings 

    def update(self, request, pk=None):
        # Your code

urls.py

from django.urls import path, include

from .views import ApplicationViewSet


app_list = ApplicationViewSet.as_view({
    'get': 'list',
    'post': 'create'
})
app_detail = ApplicationViewSet.as_view({
    'get': 'retrieve',
    'post': 'update',  # POST instead PUT
})


urlpatterns = [
    path('application/', app_list, name='application-list'),
    path('application/<int:pk>/', app_detail, name='application-detail'),
]
like image 179
Lucas Weyne Avatar answered Oct 11 '22 17:10

Lucas Weyne


I just had to do this and found that this works when setting up the router in urls.py:

router = routers.SimpleRouter()
# Post Edit route.
router.routes[2].mapping['post'] = 'partial_update'
router.register(r'things', ThingViewSet)

The alternative is you build your own Router. Which isn't to bad either.

For reference here's the bit of code that defines the array this changes.

like image 42
whytheplatypus Avatar answered Oct 11 '22 18:10

whytheplatypus