前言
Django
developer 转 Django REST framework
developer后会发现在Django
原来基础上的urlpatterns path基础上又多了一种router.register
本来呢以为Django
的部分就用沿用urlpatterns path,而DRF
部分就是用router.register,但后来看Django REST framework
官方文档(Tutorial 3: Class-based Views-英文文档|Tutorial 3: Class-based Views-中文文档)的时候,发现事情没有这么简单。
内容
传统方式
先说结论,当我们开发的api
是继承自APIView
的时候,那就使用传统的Django
CBV
,通过调用as_view
方法来实现在urlpatterns path中注册路由,就如官方文档中的例子
from django.conf.urls import url
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views
urlpatterns = [
url(r'^snippets/$', views.SnippetList.as_view()),
url(r'^snippets/(?P[0-9]+)/$', views.SnippetDetail.as_view()),
]
urlpatterns = format_suffix_patterns(urlpatterns)
SnippetDetail
的定义如下:
class SnippetDetail(APIView):
"""
检索,更新或删除一个snippet示例。
"""
def get_object(self, pk):
try:
return Snippet.objects.get(pk=pk)
except Snippet.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
snippet = self.get_object(pk)
serializer = SnippetSerializer(snippet)
return Response(serializer.data)
def put(self, request, pk, format=None):
snippet = self.get_object(pk)
serializer = SnippetSerializer(snippet, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk, format=None):
snippet = self.get_object(pk)
snippet.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
新方式
而继承专属Django REST framework
的ViewSet
,便是用router.register
注册路由。
继承自ModelViewSet
或者GenericViewSet
等以ViewSet
为后缀的都算。
具体看参考Django REST framework
官方文档(Tutorial 6: ViewSets & Routers-英文文档|Tutorial 6: ViewSets & Routers-中文文档)
下面提取关键代码
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from snippets import views
# Create a router and register our viewsets with it.
router = DefaultRouter()
router.register(r'snippets', views.SnippetViewSet)
router.register(r'users', views.UserViewSet)
# The API URLs are now determined automatically by the router.
urlpatterns = [
path('', include(router.urls)),
]
其中SnippetViewSet定义如下,看可以到SnippetViewSet是继承自ModelViewSet
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import permissions
from rest_framework import viewsets
class SnippetViewSet(viewsets.ModelViewSet):
"""
This viewset automatically provides `list`, `create`, `retrieve`,
`update` and `destroy` actions.
Additionally we also provide an extra `highlight` action.
"""
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly]
@action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer])
def highlight(self, request, *args, **kwargs):
snippet = self.get_object()
return Response(snippet.highlighted)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
两种方式之间的关系
Django
的urlpatterns path和Django REST framework
的router.register并不是对立关系,不需要二选一,DRF
的方式本质是对Django
方式的包装,至于为什么要这么包装是为了更好的组织api
同样我们可以在继承自ViewSet
的类中使用传统的as_view
方法,这在上诉文档中有详细解答,大致意思就是要显性的写明as_view
方法的action
参数,该参数是一个字典类型,申明诸如get、post、patch等方法和具体函数之间的映射关系。
from snippets.views import SnippetViewSet, UserViewSet, api_root
from rest_framework import renderers
snippet_list = SnippetViewSet.as_view({
'get': 'list',
'post': 'create'
})
snippet_detail = SnippetViewSet.as_view({
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy'
})
snippet_highlight = SnippetViewSet.as_view({
'get': 'highlight'
}, renderer_classes=[renderers.StaticHTMLRenderer])
user_list = UserViewSet.as_view({
'get': 'list'
})
user_detail = UserViewSet.as_view({
'get': 'retrieve'
})