一、基于类的视图(CBV:class based view)
我们也可以使用基于类的视图编写我们的API视图,而不是基于函数。上一篇中,主要讲的是请求和响应,项目里面的视图函数都是基于函数的,并且介绍了@api_view装饰器的使用。在这篇文章中,我们要做的事把基于方法的视图改为基于类的视图。这是很强大的模式,使我们可以重用常用的功能,可以减少代码量。
1.1 使用基于类的视图重写我们的API
# quickstart/views.py from .models import Snippet from .serializers import SnippetSerializer from rest_framework.decorators import api_view from rest_framework.response import Response from rest_framework import status from rest_framework.views import APIView class SnippetList(APIView): """ 列出所有的code snippet,或者创建一个新的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)
和原来的相比,可以发现基于类的视图把各种不同的HTTP请求分离开变成单个的方法,而不是if...elif...这样的结构,所以这样处理起来会更加的高效,同样的,被另外一个视图函数也进行修改:
class SnippetDetail(APIView): """ 获取,更新或者删除一个code snippet """ def get_object(self, pk): try: return Snippet.objects.get(pk=pk) except Snippet.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) 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)
修改对应的quickstart/urls.py
from django.conf.urls import url import quickstart.views as views from rest_framework.urlpatterns import format_suffix_patterns app_name = 'qk' urlpatterns = [ # url(r'qk/snippets/$', views.snippet_list), # url(r'qk/snippets/(?P<pk>[0-9]+)/$', views.snippet_detail), url(r'qk/snippets/$', views.SnippetList.as_view()), url(r'qk/snippets/(?P<pk>[0-9]+)/$', views.SnippetDetail.as_view()), ] urlpatterns = format_suffix_patterns(urlpatterns)
1.2 使用混合(mixins)
使用基于类的视图的一个重大胜利是它允许我们轻松地构造可重复使用的行为。
到目前为止,我们使用的创建/检索/更新/删除操作对于我们创建的任何模型支持的API视图将非常相似。这些常见的行为是在REST框架的mixin类中实现的。
from .models import Snippet from .serializers import SnippetSerializer from rest_framework.response import Response from rest_framework import status from rest_framework import mixins from rest_framework import generics ####################基于类,继承APIView################################# # class SnippetList(APIView): # """ # 列出所有的code snippet,或者创建一个新的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) ####################基于类,使用Mixins################################# 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)
新的视图类中继承了 generic.GenericAPIView、mixins.ListModelMixin和mixins.CreatteModelMixin,类的作用看字面意思就能懂啦,mixins类为我们提供了list()和create()方法,当然,使用这两个函数需要先设置queryset和serializer_class,这点我们查看一下mixins的源码就可以看出来了,比如list方法:
class ListModelMixin(object): """ List a queryset. """ def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data)
同理,修改一下另一个视图类:
####################基于类,继承APIView################################# # class SnippetDetail(APIView): # """ # 获取,更新或者删除一个code snippet # """ # # def get_object(self, pk): # try: # return Snippet.objects.get(pk=pk) # except Snippet.DoesNotExist: # return Response(status=status.HTTP_404_NOT_FOUND) # # 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) ####################基于类,使用Mixins################################# 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)
1.3 使用通用的基于类的视图(generics)
通过使用mixin类,我们使用更少的代码重写了这些视图,但我们还可以再进一步。REST框架提供了一组已经混合好(mixed-in)的通用视图generics,我们可以使用它来简化我们的views.py模块。进一步简化到mixins类都不用了,只使用generics就可以了:
from django.shortcuts import render # Create your views here. from .models import Snippet from .serializers import SnippetSerializer from rest_framework import generics ####################基于类,使用Mixins################################# # 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################################# class SnippetList(generics.ListCreateAPIView): queryset = Snippet.objects.all() serializer_class = SnippetSerializer ####################基于类,使用Mixins################################# # 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) ####################基于类,使用generics################################# class SnippetDetail(generics.RetrieveUpdateDestroyAPIView): queryset = Snippet.objects.all() serializer_class = SnippetSerializer