1

前言

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<pk>[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 frameworkViewSet,便是用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'
})

universe_king
3.4k 声望677 粉丝