Ben RR Ben RR - 10 days ago 5
reST (reStructuredText) Question

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": "user2@users.com"
},
{
"url": "http://127.0.0.1:8000/users-minimal/1/",
"username": "user1,
"email": "user1@users.com"
}
]
}


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.

Answer

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

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

Normally DRF genereates a base_name automatically from your queryset. This is explained here under "base_name": www.django-rest-framework.org/api-guide/routers/

Your two viewsets use the same queryset so the have initially the same base_name. 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]+)/?$>]
Comments