Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to build a Django REST route that extracts multiple arguments from the URL?

I have built some Django-Rest-Framework APIs to my application. These APIs do things like return lists of Model Instances and return counts of how many models meet a pre-determined criteria.

Here is what the my ViewSet looks like:

class MyObjectsViewSet(viewsets.ViewSet):

    def retrieve(self,request,pk=None):
        queryset = ## Do something here to return a list of myObjects
        serializer = MyObjectsSerializer(queryset) ## MyObjectsSerializer not shown here.
        return Response(serializer.data,status=status.HTTP_200_OK)

And here is what my urls.py looks like for this endpoint:

router = routers.DefaultRouter()
router.register(r'myObjects', views.myObjectsViewSet, base_name="myObjects")

urlpatterns = patterns('',
    ...
    url(r'^api/', include(router.urls)),
    ...
)

The above code allows me to take a single parameter to the API and use it as the pk argument in MyObjectsViewSet.retrieve(). So when I point my browser to /api/MyObjects/60/, retrieve() is called with pk == 60. Great. Everything is awesome.

But now I need an API that will do something more. I need an API that takes two numeric parameters ("pk" and "otherArg") and calls another viewSet (MyObjectsNewView) with those two arguments. So that when I point my browser to /api/MyObjects/60/59/, MyObjectsNewView.retrieve() will be called with arguments pk == 60 and otherArg == 59.

How can I design my routes in url.py and my Viewsets to make this work?

In other cases, when I was designing non-REST urls, I used regexps and used notation like this to specify extract arguments from a URL string: (?P<MyObjectID>\d+).

It would seem like I should be able to do the same type of thing here. However, it automatically takes in the pk argument without me specifying it anywhere in the route, so now I'm confused how to add another argument to this route when the first pk argument sorta appeared magically without any regexp pattern.

like image 208
Saqib Ali Avatar asked Mar 03 '14 06:03

Saqib Ali


People also ask

What is Basename in Django URLs?

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.

Is Django GOOD FOR REST API?

Django REST framework (DRF) is a powerful and flexible toolkit for building Web APIs. Its main benefit is that it makes serialization much easier. Django REST framework is based on Django's class-based views, so it's an excellent option if you're familiar with Django.

What is ModelViewSet in Django REST framework?

ModelViewSet. The ModelViewSet class inherits from GenericAPIView and includes implementations for various actions, by mixing in the behavior of the various mixin classes. The actions provided by the ModelViewSet class are .list() , .retrieve() , .create() , .update() , .partial_update() , and .destroy() .


2 Answers

Without using regex drf provides path with type prefix, if you don't have strict validation for "pk" and "otherArg". So,

Step 1: In urlpatters,

path('/api/<int:id>/<otherArg>/', YourViewset.action_name, name="action_name")

Step 2: In Viewset

@action(methods=['get'],detail=True/False)

Step 3: Below @action

def action_name(self, id, otherArg):

Now use id, otherArg/s in function. Make changes and use.

like image 61
Henshal B Avatar answered Sep 24 '22 18:09

Henshal B


Write a custom detail route and change the signature to pass in the additional kwarg you intend to use. Then, customize the url path.

from rest_framework.decorators import detail_route
from rest_framework import viewsets

class MyObjectsViewSet(viewsets.ViewSet):

    @detail_route(methods=['get'], url_path='(?P<oid>\d+)')
    def get_with_objectid(self,request,pk=None, oid=None):
        queryset = ## Do something here with oid to return a list of myObjects
        serializer = MyObjectsSerializer(queryset) ## MyObjectsSerializer not shown here.
        return Response(serializer.data,status=status.HTTP_200_OK)

Now your viewset will work with /api/MyObjects/60/59/ where pk=60 and oid=59. If the url_path was instead url_path='/thisthing/(?P(<oid>\d+)' then the viewset would register /api/MyObjects/60/thisthing/59.

like image 27
Patti Avatar answered Sep 20 '22 18:09

Patti