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.
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 .
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.
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.
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() .
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 Viewset
s 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]+)/?$>]
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