Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiples @detail_route with the same url_path #4264

Closed
wants to merge 1 commit into from
Closed

Multiples @detail_route with the same url_path #4264

wants to merge 1 commit into from

Conversation

humitos
Copy link

@humitos humitos commented Jul 14, 2016

Description

This PR allows you to have multiple class method in your ViewSet each one with the same 'url_path' but different HTTP method using @detail_route, e.g.:

 class MyViewSet(viewsets.GenericViewset):

     @detail_route(methods=['get'], url_path='cancel')
 def cancel_get(self, *args, **kwargs):
         pass

     @detail_route(methods=['post'], url_path='cancel')
     def cancel_post(self, *args, **kwargs):
         pass

Example code

After creating a django-rest-project, this is all the code needed to test with simple curl hits:

# urls.py

from __future__ import division, print_function, unicode_literals

from rest_framework import routers, serializers, viewsets
from rest_framework.decorators import detail_route
from rest_framework.permissions import AllowAny
from rest_framework.response import Response

from django.conf.urls import include, url
from django.contrib import admin
from django.contrib.auth.models import User


class UserSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = User
        fields = ('url', 'username', 'email', 'is_staff')


class UserViewSet(viewsets.GenericViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [AllowAny]

    @detail_route(methods=['get'], url_path='test')
    def test_get(self, *args, **kwargs):
        return Response('GET method')

    @detail_route(methods=['post'], url_path='test')
    def test_post(self, *args, **kwargs):
        return Response('POST method')

router = routers.DefaultRouter()
router.register(r'users', UserViewSet, base_name='userview')

urlpatterns = [
    url(r'^', include(router.urls)),
    url(r'^admin/', admin.site.urls),
]

GET to my endpoint:

$ curl -i  -XGET 'http://localhost:8000/users/1/test/' 
HTTP/1.0 200 OK
Date: Thu, 14 Jul 2016 23:22:36 GMT
Server: WSGIServer/0.2 CPython/3.4.3
X-Frame-Options: SAMEORIGIN
Allow: GET, POST, HEAD, OPTIONS
Vary: Accept, Cookie
Content-Type: application/json

"GET method"

POST to my endpoint:

$ curl -i  -XPOST 'http://localhost:8000/users/1/test/'
HTTP/1.0 200 OK
Date: Thu, 14 Jul 2016 23:23:05 GMT
Server: WSGIServer/0.2 CPython/3.4.3
X-Frame-Options: SAMEORIGIN
Allow: GET, POST, HEAD, OPTIONS
Vary: Accept, Cookie
Content-Type: application/json

"POST method"

Using the original routers.py file (from master)

GET to the endpoint:

$ curl -i  -XGET 'http://localhost:8000/users/1/test/' 
HTTP/1.0 200 OK
Date: Thu, 14 Jul 2016 23:26:09 GMT
Server: WSGIServer/0.2 CPython/3.4.3
X-Frame-Options: SAMEORIGIN
Allow: GET, HEAD, OPTIONS
Vary: Accept, Cookie
Content-Type: application/json

"GET method"

POST to the endpoint

$ curl -i  -XPOST 'http://localhost:8000/users/1/test/' 
HTTP/1.0 405 Method Not Allowed
Date: Thu, 14 Jul 2016 23:26:12 GMT
Server: WSGIServer/0.2 CPython/3.4.3
X-Frame-Options: SAMEORIGIN
Allow: GET, HEAD, OPTIONS
Vary: Accept, Cookie
Content-Type: application/json

{"detail":"Method \"POST\" not allowed."}

Notes

I ran the tests using tox and some of them are not passing. As I'm new as collaborator of drf, before to start going deep into the code and try to fix these and make all the test pass (that will take me some time to research), I would like to know if the idea is something that you consider at least valid.

I mean, maybe there is something that I'm not seeing or this should be done in another way.

What do you think?

This commit allows you to have multiple class method in your ViewSet
each one with the same 'url_path' but different HTTP method, e.g.:

     class MyViewSet(viewsets.GenericViewset):

         @detail_route(methods=['get'], url_path='cancel')
	 def cancel_get(self, *args, **kwargs):
             pass

     	 @detail_route(methods=['post'], url_path='cancel')
     	 def cancel_post(self, *args, **kwargs):
             pass
@kevin-brown
Copy link
Member

This is a duplicate of #2820, and there are quite a few reasons mentioned in that ticket for not including this in the core. I'm going to leave this open for now to let others chime in, opinions may have changed over the last year.

@xordoquy
Copy link
Collaborator

I'd suggest to make this a 3rd party.
Closing it for the reason mentioned in #2820, in particular being in favor of having an explicit detail/list_route method that gets both POST and GET and do the routing itself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants