OneRaynyDay
OneRaynyDay

Reputation: 3968

Django Rest Framework: One URL for POST, one URL for GET on the same APIView

I am currently writing a POST request to create an A record:

client.post(url, {'name' : 'foo', 'another_field' : 2})

And on the server side, on the view, I have:

class AQuery(generics.GenericAPIView):
    queryset = A.objects.all()
    serializer_class = ASerializer

    def get(self, request, param1, param2, format=None):
        ... # do some special query based off of param1 and 2.

    def post(self, request, format=None):
        serializer = self.get_serializer(data=request.data, many=False) 
        if serializer.is_valid():
            serializer.save()
            return Response(...)
        return Response(...)

As you can see, I want param1 and param2 to be captured in the url, and so in the urls.py:

urlpatterns = [
    # for GET
    path('A/<str:param1>/<path:param2>', views.AQuery.as_view(), name='A query GET')
    # for POST
    path('A', views.AQuery.as_view(), name='A query POST')
    ...
]

Obviously, I can add the param1 and param2 to the post function def, but they would just be there, with no purpose. Seems like bad code smell to me.

How would I implement something like this, and explicitly disallow the client to make a POST request to A/param1/param2, and explicitly disallow the client to make a GET request to A/?

What I've tried

I tried to turn the View into a viewsets.ModelViewSet, and added the following:

a_get_query = AQuery.as_view({'get':'get'})
a_post_query = AQuery.as_view({'post':'post'})

and in my urls.py:

urlpatterns = [
        # for GET
        path('A/<str:param1>/<path:param2>', views.a_get_query, name='A query GET')
        # for POST
        path('A', views.a_post_query, name='A query POST')
        ...
    ]

But I get the following error:

TypeError: The actions argument must be provided when calling .as_view() on a ViewSet. For example .as_view({'get': 'list'})

and I'm not quite sure where it's happening.

Upvotes: 1

Views: 1873

Answers (1)

Will Keeling
Will Keeling

Reputation: 23024

This approach works as expected for me. I had to make a couple of minor changes however:

1) I had to change the names of the functions from get and post to do_get and do_post. Django seems to do something special when the functions are named the same as the HTTP methods themselves.

2) I inlined calling as_view() directly in the urls.py.

So after doing that:

urlpatterns = [
        # for GET
        path('A/<str:param1>/<path:param2>', views.AQuery.as_view({'get':'do_get'}), name='A query GET')
        # for POST
        path('A', views.AQuery.as_view({'post':'do_post'}), name='A query POST')
        ...
    ]

The result:

GET   A/param1/param2  -> 200 OK
GET   A/               -> 405 Method Not Allowed

POST  A/param1/param2  -> 405 Method Not Allowed
POST  A/               -> 200 OK

Upvotes: 2

Related Questions