Django Rest Framework with multiple Viewsets and R

2019-06-20 10:26发布

问题:

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.

回答1:

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]+)/?$>]