Adding more views to a Router or viewset (Django-Rest-Framework)
Solution 1
The answer given by mariodev above is correct, as long as you're only looking to make GET
requests.
If you want to POST
to a function you're appending to a ViewSet, you need to use the action
decorator:
from rest_framework.decorators import action, link
from rest_framework.response import Response
class MyObjectsViewSet(viewsets.ViewSet):
# For GET Requests
@link()
def get_locations(self, request):
""" Returns a list of location objects somehow related to MyObject """
locations = calculate_something()
return Response(locations)
# For POST Requests
@action()
def update_location(self, request, pk):
""" Updates the object identified by the pk """
location = self.get_object()
location.field = update_location_field() # your custom code
location.save()
# ...create a serializer and return with updated data...
Then you would POST
to a URL formatted like:
/myobjects/123/update_location/
http://www.django-rest-framework.org/api-guide/viewsets/#marking-extra-actions-for-routing has more information if you're interested!
Solution 2
You can now do this with the list_route and detail_route decorators: http://www.django-rest-framework.org/api-guide/viewsets/#marking-extra-actions-for-routing
For example:
from rest_framework.decorators import list_route
from rest_framework.response import Response
...
class MyObjectsViewSet(viewsets.ViewSet):
...
@list_route()
def locations(self, request):
queryset = get_locations()
serializer = LocationSerializer(queryset, many=True)
return Response(serializer.data)
Solution 3
You define method like you do now, but you need to use the same url as method name and add link decorator, so for
/myobjects/123/locations/
You add method like this
@link(permission_classes=[...])
def locations(self, request, pk=None):
...
and router will pick it automatically.
Solution 4
From Routing to extra methods on a ViewSet:
I think you may need to route the method by hand, i.e. The Old-Fashioned Way™.
First pull the method out as a separate view:
set_password_view = UserViewSet.as_view({'post': 'set_password'})
(or such)
Then assign your URL:
url(r'^users/username_available/$', set_password_view, name-=...)
(Or such)
There's a related question on SO.
Related videos on Youtube
Kurtis
Freelance web-application developer who tries to specialize in Python/Django but is always exploring new technologies.
Updated on July 22, 2022Comments
-
Kurtis almost 2 years
Essentially, I'm trying to find a good way to attach more views to a Router without creating a custom Router. What's a good way to accomplish this?
Here is something sort of equivalent to what I'm trying to accomplish. Variable names have been changed and the example method I want to introduce is extremely simplified for the sake of this question.
Router:
router = routers.SimpleRouter(trailing_slash=False) router.register(r'myobjects', MyObjectViewSet, base_name='myobjects') urlpatterns = router.urls
ViewSet
class MyObjectsViewSet(viewsets.ViewSet): """ Provides API Methods to manage MyObjects. """ def list(self, request): """ Returns a list of MyObjects. """ data = get_list_of_myobjects() return Response(data) def retrieve(self, request, pk): """ Returns a single MyObject. """ data = fetch_my_object(pk) return Response(data) def destroy(self, request, pk): """ Deletes a single MyObject. """ fetch_my_object_and_delete(pk) return Response()
One example of another method type I need to include. (There are many of these):
def get_locations(self, request): """ Returns a list of location objects somehow related to MyObject """ locations = calculate_something() return Response(locations)
The end-result is that the following URL would work correctly and be implemented 'cleanly'.
GET example.com/myobjects/123/locations
-
Kurtis over 10 yearsQuick and very precise answer. The only thing I would add is "from rest_framework.decorators import link". Thanks!
-
Kurtis over 10 yearsIf possible, could you (or anyone) also show me an example of adding a method to the Router which is not 'contained' within my ViewSet?
-
psychok7 over 9 yearsi am trying to upload a file to a custom method. do you know how i can achieve that?
-
rtindru over 8 yearsIs
@action
deprecated - The link suggests methods as arguments to the@detail_route
decorator. -
Roel over 7 yearsHi im new to django, what would be the end point for this? thanks!
-
cezar over 6 years@ShiftN'Tab If you have
myobjects-list
andmyobjects-detail
, then this will bemyobjects-locations
. -
NFern almost 5 years'@detail_route' and '@list_route' are marked for deprecation. django-rest-framework.org/community/3.8-announcement/…