Django Rest Framework Routers with multiple viewse

2019-04-21 15:29发布

问题:

I am attempting to create an API using DRF 3.1. I started by creating manangement functions using ModelViewSet and registering these with a DefaultRouter. This worked perfectly and the default API root view generated the appropriate links when viewed in a browser. I then created more restricted viewsets for standard users using ReadOnlyModelViewSet, the problem occurred when I tried to register these restricted viewsets with the router as the ReadOnlyModelViewSets appear to overwrite the links on the API root view. The generated urls appear to work when typed into the browser but the links on the API root view are not as I expected

serializers.py

class AdminUnitSerializer(serializers.HyperlinkedModelSerializer):
'''
model serializer for admin users containing all fields

'''

url = HyperlinkedIdentityField(view_name='unit-detail')

class Meta:
    model = Unit
    fields = ('url', 'id', 'name', 'symbol', 'converters_from', 'converters_to')

class UserUnitSerializer(serializers.HyperlinkedModelSerializer):
'''
model serializer for standard users containing restricted set of fields

'''

url = HyperlinkedIdentityField(view_name='unit-detail')

class Meta:
    model = Unit
    fields = ('url', 'id', 'name', 'symbol')

views.py

class AdminUnitViewSet(viewsets.ModelViewSet):
'''
simple viewset providing CRUD operations on unit objects

'''

queryset = models.Unit.objects.all()
serializer_class = serializers.AdminUnitSerializer
permission_classes = (permissions.IsAdminUser,)

class UserUnitViewSet(viewsets.ReadOnlyModelViewSet):
'''
read only view set providing list and detail views for unit objects

'''

queryset =  models.Unit.objects.all()
serializer_class = serializers.UserUnitSerializer
permission_classes(permissions.IsAuthenticated,)

urls.py

router.register(r'manangeunits', views.AdminUnitViewSet)
router.register(r'readunits', views.UserUnitViewSet)

urlpatterns = [
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^rest-auth/', include('rest_auth.urls')),
url(r'^', include(router.urls))
]

this produces a default API root view like this

{
"manangeunits": "http://localhost:8000/readunits/",
"readunits": "http://localhost:8000/readunits/",
}

while what I am looking for is this

{
"manangeunits": "htt://localhost:8000/manageunits/",
"readunits": "htt://localhost:8000/readunits/",
}

the url 'htt://localhost:8000/manageunits/' is valid and accessible when the address is typed into the browser it just does not appear on the API root view

any help would be greatly appreciated

回答1:

I had the same issue and luckily found a solution.

Extend your urls.py as following:

router.register(r'manangeunits', views.AdminUnitViewSet, base_name='manangeunits')
router.register(r'readunits', views.UserUnitViewSet, base_name='readunits')

I don't know the magic behind this, but it works for me :)



回答2:

The answer from Dario Behringer is correct. But it is not just magic.

The important part is the addition of the explicit base_name. Normally DRF generates a base_name automatically from your queryset. This is explained here under "base_name": www.django-rest-framework.org/api-guide/routers/. The queryset of AdminUnitViewSet and UserUnitViewSet is the same internally.

This leads to the effect that the later registered ViewSet will overwrite the former registered ViewSet when there is no explicit new base_name registered.

The base_name is responsible for creating a named URL in Django. If there is more than one named URL with the same name Django will basically only consider the latest defined: https://docs.djangoproject.com/en/1.10/topics/http/urls/#naming-url-patterns

It is basically the same problem as explained in question Django Rest Framework with multiple Viewsets and Routers for the same object