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
APIView allow us to define functions that match standard HTTP methods like GET, POST, PUT, PATCH, etc. Viewsets allow us to define functions that match to common API object actions like : LIST, CREATE, RETRIEVE, UPDATE, etc.
While regular views act as handlers for HTTP methods, viewsets give you actions, like create or list . The great thing about viewsets is how they make your code consistent and save you from repetition. Every time you write views that should do more than one thing, a viewset is the thing that you want to go for.
Django REST framework allows you to combine the logic for a set of related views in a single class, called a ViewSet . In other frameworks you may also find conceptually similar implementations named something like 'Resources' or 'Controllers'.
basename - The base to use for the URL names that are created. If unset the basename will be automatically generated based on the queryset attribute of the viewset, if it has one. Note that if the viewset does not include a queryset attribute then you must set basename when registering the viewset.
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!
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With