Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Rest Framework with multiple Viewsets and Routers for the same object

I am having trouble defining different view sets for the same object using Django Rest Framework. Following is a minimal example to reproduce the issue, based on DRF Quickstart. I am using python 3.5 and the latest DRF.

tutorial/quickstart/serializers.py

from django.contrib.auth.models import User
from rest_framework import serializers


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('url', 'username', 'email')

class UserMinimalSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('url', 'username')

tutorial/quickstart/views.py

from django.contrib.auth.models import User
from rest_framework import viewsets
from tutorial.quickstart.serializers import UserSerializer, UserMinimalSerializer

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all().order_by('-date_joined')
    serializer_class = UserSerializer


class UserMinimalViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all().order_by('-date_joined')
    serializer_class = UserMinimalSerializer

tutorial/urls.py

from django.conf.urls import url, include
from rest_framework import routers
from tutorial.quickstart import views

router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'users-minimal', views.UserMinimalViewSet) 

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

When running the server and GETting the root, you end up with:

HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "users": "http://127.0.0.1:8000/users-minimal/",
    "users-minimal": "http://127.0.0.1:8000/users-minimal/"
}

Note both users and users-minimal point to .../users-minimal/.

Also, when accessing http://HOST:PORT/users/ you get:

HTTP 200 OK
Allow: GET, POST, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "count": 2,
    "next": null,
    "previous": null,
    "results": [
        {
            "url": "http://127.0.0.1:8000/users-minimal/2/",
            "username": "user2",
            "email": "[email protected]"
        },
        {
            "url": "http://127.0.0.1:8000/users-minimal/1/",
            "username": "user1,
            "email": "[email protected]"
        }
    ]
}

Note the urls point to .../users-minimal/.

Final note: my question is somewhat similar to this one, but the suggested solution did not work for me, nor did it suggest why it should work in the first place.

like image 409
Ben RR Avatar asked Nov 23 '16 07:11

Ben RR


People also ask

Should I use APIView or Viewset?

APIView and ViewSet all have their right use cases. But most of the time, if you are only doing CRUD on resources, you can directly use ViewSets to respect the DRY principle. But if you are looking for more complex features, you can go low-level because after all, viewsets are also a subclass of APIView .

What is difference between Viewset and ModelViewSet?

It is more rigid than a generic APIView but it takes away from you some boilerplate/manual config that you would have to repeat again and again in most cases. One step further is the ModelViewSet , which is an extension of the ViewSet for when you are working with Django models.

Do we need Serializers in Django REST framework?

Serializers in Django REST Framework are responsible for converting objects into data types understandable by javascript and front-end frameworks. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.

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() .


1 Answers

Short answer: You have to add the basename parameter to your route to users-minimal:

router = routers.DefaultRouter()
router.register(r'users', UserViewSet)
router.register(r'users-minimal', UserMinimalViewSet, basename='usersminimal')

Normally DRF genereates a basename automatically from your queryset. This is explained in the DRF routers docs, search for basename.

Your two Viewsets use the same queryset so the have initially the same basename. That leads to the problems which you have seen, that the later registered ViewSet will overwrite the routes from the former registered ViewSet. You can see this in action when you change the order of the router.register in your example.

You can see the base names of your routes when you test the code directly in the shell:

from rest_framework import routers
from tutorial.quickstart import views

router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'users-minimal', views.UserMinimalViewSet)


> routers.urls
[<RegexURLPattern user-list ^minimal/$>,
<RegexURLPattern user-list ^minimal\.(?P<format>[a-z0-9]+)/?$>,
<RegexURLPattern user-detail ^minimal/(?P<pk>[^/.]+)/$>,
<RegexURLPattern user-detail ^minimal/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$>,
<RegexURLPattern user-list ^users/$>,
<RegexURLPattern user-list ^users\.(?P<format>[a-z0-9]+)/?$>,
<RegexURLPattern user-detail ^users/(?P<pk>[^/.]+)/$>,
<RegexURLPattern user-detail ^users/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$>,
<RegexURLPattern api-root ^$>,
<RegexURLPattern api-root ^\.(?P<format>[a-z0-9]+)/?$>]
like image 50
DanEEStar Avatar answered Nov 07 '22 02:11

DanEEStar