基于类的 views

我们之前讨论的都是基于函数的 views ,也就是说所有的 views 都是以一个个函数来写的。但是,现在,我们要讲的就是以类的形式来编写 views,这样的话,每个 views 都是一个类,这有助于我们更好得重用代码,从而更好得实现 DRY。

使用基于类的 views 重写 API

打开我们的 views.py 文件,我们将它修改成:

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from django.http import Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status


class SnippetList(APIView):

    List all snippets, or create a new snippet.

    def get(self, request, format=None):
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):
        serializer = SnippetSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class SnippetDetail(APIView):

    Retrieve, update or delete a snippet instance.

    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)


好的,是不是看上去很赞。和基于函数的 views 一样的简洁,甚至更好一些。同样得,我们也需要对 urls.py 进行修改,修改成这样:

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)


现在,重新运行一下你的服务试试,是不是完美得在工作。

使用 mixins

使用基于类的 views 的最大好处就是我们可以轻松重用通用代码,尤其是像增删改查这样的操作,基本上都是类似的。这些行为在 REST框架 中都通过 mixins 类被实现了。

让我们来看看使用 mixins 类实现的组件吧,还是拿我们的 views.py 模块为例:

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import mixins
from rest_framework import generics

class SnippetList(mixins.ListModelMixin,
                  mixins.CreateModelMixin,
                  generics.GenericAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)


这里,我们集成了 generics.GenericAPIView ,然后再添加了 ListModelMixin 和 CreateModelMixin,基类提供了核心的功能,然后 mixin 类提供了 .list 和 .create 功能。然后我们实现 get 和 post 方法用于响应请求。

class SnippetDetail(mixins.RetrieveModelMixin,
                    mixins.UpdateModelMixin,
                    mixins.DestroyModelMixin,
                    generics.GenericAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)


我们用同样的方式实现 SnippetDetail。

使用基于通用类的 views

使用 mixin类 之后,我们发现代码节省了很多,但是,我们还可以更进一步。REST框架 提供了一些 mixed-in 的通用 view,可以帮助我们实现更简洁的 views。

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import generics


class SnippetList(generics.ListCreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer


class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer


哇喔,看看我们的代码,多么简介啊,这才是 django 的魅力啊!代码良好,结构清晰,阅读顺畅。