django-rest-framework学习之路-7-视图集和路由器


1、合并list与detail

我们之前都是两个视图类来表示一组操作,比如SnippetList和SnippetDetail,我们可以合二为一

使用ViewSets类来完成合二为一,称之为视图集

重写snippets/views.py

使用UserViewSet类替换UserList和UserDetail

class UserViewSet(viewsets.ReadOnlyModelViewSet):
    """
    此视图自动提供`list`和`detail`操作。
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

继承ReadOnlyModelViewSet,表示只读

使用SnippetViewSet替换SnippetList,SnippetDetail和SnippetHighlight

from django.contrib.auth.models import User
from rest_framework import permissions, renderers, viewsets
from rest_framework.decorators import api_view, action
from rest_framework.response import Response
from rest_framework.reverse import reverse

from snippets.models import Snippet
from snippets.permissions import IsOwnerOrReadOnly
from snippets.serializers import SnippetSerializer, UserSerializer


@api_view(['GET'])
def api_root(request, format=None):
    return Response({
        'users': reverse('user-list', request=request, format=format),
        'snippets': reverse('snippet-list', request=request, format=format),
    })


class UserViewSet(viewsets.ReadOnlyModelViewSet):
    """
    此视图自动提供`list`和`detail`操作。
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer


class SnippetViewSet(viewsets.ModelViewSet):
    """
    此视图自动提供`list`,`create`,`retrieve`,`update`和`destroy`操作。

    另外我们还提供了一个额外的`highlight`操作。
    """
    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)

使用action装饰器自定义操作,detail=True表示detail方式

提示:ViewSet类与View类几乎相同,不同之处在于它们提供诸如read或update之类的操作,而不是get或put等方法处理程序。

因为ViewSet类是提供read,update之类的方式操作数据,而不是get,put等方式,因此我们的路由就需要改变一下,填充一下对应请求方式对应的处理函数

在snippets/views.py添加对应关系

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'
})

所以snippets/views.py变成了

from django.contrib.auth.models import User
from rest_framework import permissions, renderers, viewsets
from rest_framework.decorators import api_view, action
from rest_framework.response import Response
from rest_framework.reverse import reverse

from snippets.models import Snippet
from snippets.permissions import IsOwnerOrReadOnly
from snippets.serializers import SnippetSerializer, UserSerializer


@api_view(['GET'])
def api_root(request, format=None):
    return Response({
        'users': reverse('user-list', request=request, format=format),
        'snippets': reverse('snippet-list', request=request, format=format),
    })


class UserViewSet(viewsets.ReadOnlyModelViewSet):
    """
    此视图自动提供`list`和`detail`操作。
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer


class SnippetViewSet(viewsets.ModelViewSet):
    """
    此视图自动提供`list`,`create`,`retrieve`,`update`和`destroy`操作。

    另外我们还提供了一个额外的`highlight`操作。
    """
    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)


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'
})

那对应的路由就改变了

所以snippets/urls.py变成了

from django.urls import re_path
from rest_framework.urlpatterns import format_suffix_patterns

from snippets import views

urlpatterns = [
    re_path(r'^$', views.api_root),
    re_path(r'^snippets/$', views.snippet_list, name='snippet-list'),
    re_path(r'^snippets/(?P[0-9]+)/$', views.snippet_detail, name='snippet-detail'),
    re_path(r'^snippets/(?P[0-9]+)/highlight/$', views.snippet_highlight, name='snippet-highlight'),
    re_path(r'^users/$', views.user_list, name='user-list'),
    re_path(r'^users/(?P[0-9]+)/$', views.user_detail, name='user-detail')
]

urlpatterns = format_suffix_patterns(urlpatterns)

打开浏览器,和之前是一样的效果

使用路由

因为我们使用了ViewSet类而不是View类,既然它可以把list和detail合二为一,那么路由也有简单的处理方式,而不是这么麻烦

我们可以使用router路由器来直接完成对应关系

所以snippets/urls.py变成了

from django.conf.urls import include
from django.urls import re_path
from rest_framework.routers import DefaultRouter

from snippets import views

# 创建路由器并注册我们的视图。
router = DefaultRouter()
router.register(r'snippets', views.SnippetViewSet)
router.register(r'users', views.UserViewSet)

# API URL现在由路由器自动确定。
urlpatterns = [
    re_path(r'^', include(router.urls)),
]

视图(views)vs视图集(viewsets)之间的权衡

使用视图集可以是一个非常有用的抽象。它有助于确保URL约定在你的API中保持一致,最大限度地减少编写所需的代码量,让你能够专注于API提供的交互和表示,而不是URLconf的细节。

这并不意味着采用视图集总是正确的方法。在使用基于类的视图而不是基于函数的视图时,有一个类似的权衡要考虑。使用视图集不像单独构建视图那样明确。

学习链接: https://www.w3cschool.cn/lxraw/lxraw-9gjo35og.html