前戏

在前面几篇文章里,我们写了get请求,post请求,put请求,在来写个delete请求,大概如下。

class BookView(APIView):  # 查询所有的数据和post方法

    def get(self, request):
book_queryset = Book.objects.all()
# 拿出来的是一个queryset,用序列化器进行序列化
ser_obj = BookSerializer(book_queryset, many=True)
return Response(ser_obj.data) # 序列化后的数据在data里 def post(self, request):
# 确定数据类型以及数据结构
# 对前端传来的数据进行校验
book_obj = request.data # post传来的数据
ser_obj = BookSerializer(data=book_obj) # 有data参数,表示反序列化
if ser_obj.is_valid():
ser_obj.save()
return Response(ser_obj.validated_data)
return Response(ser_obj.errors) # 返回错误 class BookEditView(APIView): # 查询单条数据和put、delete方法
def get(self, request, id):
book_obj = Book.objects.filter(id=id).first()
ser_obj = BookSerializer(book_obj) # 查询出的是一条数据,不需要加 many=True
return Response(ser_obj.data) def put(self, request, id):
book_obj = Book.objects.filter(id=id).first()
# instance必传,data=request.data前端传的参数,partial=True部分修改
ser_obj = BookSerializer(instance=book_obj, data=request.data, partial=True)
if ser_obj.is_valid():
ser_obj.save()
return Response(ser_obj.data) # 返回数据,注意不是ser_obj.validated_data
return Response(ser_obj.errors) def delete(self, request, id):
book_obj = Book.objects.filter(id=id).first()
if not book_obj:
return Response('删除的对象不存在')
book_obj.delete()
return Response('Success')

路由

urlpatterns = [
url(r'^book/$', BookView.as_view()),
url(r'^book/(?P<id>\d+)', BookEditView.as_view()),
]

这个视图只是实现了Book表的增删改查功能,如果有几十张表,我们就要写几十个对应的类,复制,粘贴,复制,粘贴。。。身为一个优秀的测试工程师。这种比较low的方法肯定不是我们干的,应该是开发干的,手动滑稽。

如果分析上面的代码,我们会发现,每个请求只要传不同的ORM语句和序列化器就可以实现了。所以我们可以对代码进行重构,面向对象的三大特性,继承,封装,多态。我们可以写一个通用的方法来继承它,每个子类都可以改写继承过来的方法,就是多态。

第一版

 from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from djangoDemo.models import Book # 导入表
from .serializers import BookSerializer class GenericAPIView(APIView): # 通用的API视图
queryset = None
serializer_class = None def get_queryset(self):
# 这里要加.all() 虽然下面的ORM查询出来的是所有的数据
# 但是由于DRF的内部机制,这里如果不加就会报错
return self.queryset.all() def get_serializer(self, *args, **kwargs):
# 不同请求的序列化器里传的参数是不一样的
return self.serializer_class(*args, **kwargs) class BookView(GenericAPIView): # 继承通用的方法
queryset = Book.objects.all()
serializer_class = BookSerializer def get(self, request):
# 调用外部的get方法
queryset = self.get_queryset() # 调用外部的序列化方法
ser_obj = self.get_serializer(queryset, many=True)
return Response(ser_obj.data) def post(self, request):
ser_obj = self.get_serializer(data=request.data)
if ser_obj.is_valid():
ser_obj.save()
return Response(ser_obj.validated_data)
return Response(ser_obj.errors)

当上面的代码执行get请求时,先执行28行,28行调用的是12,执行15行,返回的是一个queryset,然后先在自己内部找,自己内部有,也就是23行,在执行31行,调用的是17行,执行19行,返回的是serializer_class,先在自己内部找,自己内部有,也就是24行。在执行32行

上面的代码只是简单的封装了一下,还可以在次封装。

第二版

 from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from djangoDemo.models import Book # 导入表
from .serializers import BookSerializer class GenericAPIView(APIView): # 通用的API视图
queryset = None
serializer_class = None def get_queryset(self):
# 这里要加.all() 虽然下面的ORM查询出来的是所有的数据
# 但是由于DRF的内部机制,这里如果不加就会报错
return self.queryset.all() def get_serializer(self, *args, **kwargs):
# 不同请求的序列化器里传的参数是不一样的
return self.serializer_class(*args, **kwargs) class ListModelMixin(object): # get方法,查询所有数据
def list(self, request):
queryset = self.get_queryset()
ser_obj = self.get_serializer(queryset, many=True)
return Response(ser_obj.data) class CreateModelMixin(object): # post方法
def create(self, request):
ser_obj = self.get_serializer(data=request.data)
if ser_obj.is_valid():
ser_obj.save()
return Response(ser_obj.validated_data)
return Response(ser_obj.errors) class BookView(GenericAPIView, ListModelMixin, CreateModelMixin): # 继承
queryset = Book.objects.all()
serializer_class = BookSerializer def get(self, request):
return self.list(request) # 调用get方法 def post(self, request):
return self.create(request) # 调用post方法

上面对BookView类进行了封装处理,还有一个BookEditView类,也来进行封装处理

from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from djangoDemo.models import Book # 导入表
from .serializers import BookSerializer class GenericAPIView(APIView): # 通用的API视图
queryset = None
serializer_class = None def get_queryset(self):
# 这里要加.all() 虽然下面的ORM查询出来的是所有的数据
# 但是由于DRF的内部机制,这里如果不加就会报错
return self.queryset.all() def get_serializer(self, *args, **kwargs):
# 不同请求的序列化器里传的参数是不一样的
return self.serializer_class(*args, **kwargs) class RetrieveModelMixin(object): # get方法,查询单条数据
def retrieve(self, request, id):
# 先查询出所有,在过滤,在取第一个
book_obj = self.get_queryset().filter(id=id).first()
ser_obj = self.get_serializer(book_obj)
return Response(ser_obj.data) class UpdateModelMixin(object): # put方法
def update(self, request, id):
book_obj = self.get_queryset().filter(id=id).first()
ser_obj = self.get_serializer(instance=book_obj, data=request.data, partial=True)
if ser_obj.is_valid():
ser_obj.save()
return Response(ser_obj.data) # 返回数据,注意不是ser_obj.validated_data
return Response(ser_obj.errors) class DestroyModelMixin(object): # delete方法
def destoy(self, request, id):
book_obj = self.get_queryset().filter(id=id).first()
if not book_obj:
return Response('删除的对象不存在')
book_obj.delete()
return Response('Success') class BookEditView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin): # 查询单条数据和put、delete方法
queryset = Book.objects.all()
serializer_class = BookSerializer def get(self, request, id):
return self.retrieve(request, id) def put(self, request, id):
return self.update(request, id) def delete(self, request, id):
return self.destoy(request, id)

这样我们就完成了所有请求的封装

在来看下,每个BookView类和BookEditView类都继承了好几个类,如果以后还有其他的类,都要继承这几个类,多麻烦,我们可以写两个单独的类,来继承,以后所有的类都继承这两个类就可以了

第三版

from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from djangoDemo.models import Book # 导入表
from .serializers import BookSerializer class GenericAPIView(APIView): # 通用的API视图
queryset = None
serializer_class = None def get_queryset(self):
# 这里要加.all() 虽然下面的ORM查询出来的是所有的数据
# 但是由于DRF的内部机制,这里如果不加就会报错
return self.queryset.all() def get_serializer(self, *args, **kwargs):
# 不同请求的序列化器里传的参数是不一样的
return self.serializer_class(*args, **kwargs) class ListModelMixin(object): # get方法,查询所有数据
def list(self, request):
queryset = self.get_queryset()
ser_obj = self.get_serializer(queryset, many=True)
return Response(ser_obj.data) class CreateModelMixin(object): # post方法
def create(self, request):
ser_obj = self.get_serializer(data=request.data)
if ser_obj.is_valid():
ser_obj.save()
return Response(ser_obj.validated_data)
return Response(ser_obj.errors) class RetrieveModelMixin(object): # get方法,查询单条数据
def retrieve(self, request, id):
# 先查询出所有,在过滤,在取第一个
book_obj = self.get_queryset().filter(id=id).first()
ser_obj = self.get_serializer(book_obj)
return Response(ser_obj.data) class UpdateModelMixin(object): # put方法
def update(self, request, id):
book_obj = self.get_queryset().filter(id=id).first()
ser_obj = self.get_serializer(instance=book_obj, data=request.data, partial=True)
if ser_obj.is_valid():
ser_obj.save()
return Response(ser_obj.data) # 返回数据,注意不是ser_obj.validated_data
return Response(ser_obj.errors) class DestroyModelMixin(object): # delete方法
def destoy(self, request, id):
book_obj = self.get_queryset().filter(id=id).first()
if not book_obj:
return Response('删除的对象不存在')
book_obj.delete()
return Response('Success') class ListCreateAPIView(GenericAPIView, ListModelMixin, CreateModelMixin):
pass class RetrieveUpdateDestroyAPIView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
pass class BookView(ListCreateAPIView): # 查询所有数据和添加数据的方法
queryset = Book.objects.all()
serializer_class = BookSerializer def get(self, request):
return self.list(request) # 调用get方法 def post(self, request):
return self.create(request) # 调用post方法 class BookEditView(RetrieveUpdateDestroyAPIView): # 查询单条数据和put、delete方法
queryset = Book.objects.all()
serializer_class = BookSerializer def get(self, request, id):
return self.retrieve(request, id) def put(self, request, id):
return self.update(request, id) def delete(self, request, id):
return self.destoy(request, id)

第四版

上面有两个get方法,一个是查询一条,一个是查询多条。那我们可不可以通过路由传参的方式来解决呢?只让它走一个,路由类似于这样

urlpatterns = [
# url(r'^book/$', BookView.as_view()),
# url(r'^book/(?P<id>\d+)', BookEditView.as_view()),
url(r'^book/$', BookView.as_view({"get": "list", "post": "create"})),
url(r'^book/(?P<id>\d+)', BookEditView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
]

我们知道在CBV中,在执行视图函数时会先执行dispatch方法,我们继承了APIView,来看看源码是怎样写的,APIView里的as_view方法代码如下

  def as_view(cls, **initkwargs):
"""
Store the original class on the view function. This allows us to discover information about the view when we do URL
reverse lookups. Used for breadcrumb generation.
"""
if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
def force_evaluation():
raise RuntimeError(
'Do not evaluate the `.queryset` attribute directly, '
'as the result will be cached and reused between requests. '
'Use `.all()` or call `.get_queryset()` instead.'
)
cls.queryset._fetch_all = force_evaluation view = super(APIView, cls).as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs # Note: session based authentication is explicitly CSRF validated,
# all other authentication is CSRF exempt.
return csrf_exempt(view)

这里可以看到可以传参,在去看看父类的as_view方法(第十七行)

     def as_view(cls, **initkwargs):
"""
Main entry point for a request-response process.
"""
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key))

很显然,从源代码里可以看出,父类的as_view也可以接收字典类型的传参,但是,如果key在initkwargs里,就会抛出一个错误(第七行),所以我们传参肯定会报错的,既然这样,那我们可以重写as_view方法,DRF已经替我们想好了,也替我们封装了,我们只需要使用就可以了,导入

from rest_framework.viewsets import ViewSetMixin

可以点进入看91到93行的源码

for method, action in actions.items():
handler = getattr(self, action)
setattr(self, method, handler)

这里,action 就是我们传来的参数,for循环之后,method就是原来的get请求,action就是我们写的list方法({"get":"list"})。然后通过setattr,执行get时,就会去执行我们写的list方法。

我们可以写个ModelViewSet类,来继承我们的两个get方法

class ModelViewSet(ViewSetMixin, ListCreateAPIView, RetrieveUpdateDestroyAPIView):
pass

然后再写个类,让它继承这个类

class BookModelView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer

在来改写路由

from django.conf.urls import url, include
from .views import BookModelView urlpatterns = [
url(r'^book/$', BookModelView.as_view({"get": "list", "post": "create"})),
url(r'^book/(?P<id>\d+)', BookModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
]

这样我们所有的请求就都会去执行BookModelView视图了。

终极版

上面我们封装了那么多的类,其实DRF已经给我们封装好了,我们写的所有类都在下面的几个类里面,只是比我们自己写的全很多

from rest_framework import views
from rest_framework import viewsets
from rest_framework import generics
from rest_framework import mixins

我们进入viewsets里面看看最后的代码

class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
A viewset that provides default `create()`, `retrieve()`, `update()`,
`partial_update()`, `destroy()` and `list()` actions.
"""
pass

这里已经写好了,所以我们只需要继承ModelViewSet就可以了

只需要导入 from rest_framework import viewsets,继承就可以了,通过继承就可以看出,前面我们写了上百行的代码,只需要六行就实现了,这就上python的强大之处

from djangoDemo.models import Book  # 导入表
from .serializers import BookSerializer
from rest_framework import viewsets class BookModelView(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer

然后再改写路由

from django.conf.urls import url, include
from .views import BookModelView urlpatterns = [
url(r'^book/$', BookModelView.as_view({"get": "list", "post": "create"})),
url(r'^book/(?P<pk>\d+)', BookModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
]

注意:路由分组命名那里要为pk,否则会报错

这样,简单的代码就实现了我们上面所有的代码

最新文章

  1. python学习 文件操作
  2. GMap.Net开发之在地图上添加多边形
  3. Codeforces Round #384 (Div. 2)A,B,C,D
  4. InnoDB Double write
  5. CDB和PDB基本管理
  6. BitMap 内存使用优化
  7. JAVA泛型编程笔记
  8. ADO.NET连接方式
  9. 三层交换机配置说明(华为S5700设置三个网段互通)
  10. Oracle数据库常用关键字以及函数
  11. 解决XMind运行卡顿
  12. Java 问题定位工具 ——jstack
  13. Machine Learning 算法可视化实现2 - Apriori算法实现
  14. linux下使用maven修改hbase源码并重新编译
  15. Linux Tomcat安装部署项目
  16. 分布式文件系统FastDFS安装教程
  17. &lt;table&gt;标签总结(colspan跨列 ,rowspan跨行)
  18. 《MySQL必知必会》[06] 触发器
  19. Java导出List集合到txt文件中——(四)
  20. 2. RabbitMQ 服务器 之下载安装

热门文章

  1. CAD怎么算面积?这种方法你要知道
  2. 修改vscode的文件,对应的磁盘文件不改变
  3. 初级模拟电路:4-1 BJT交流分析概述
  4. java获取月的第一天和最后一天
  5. javascript ES6 新特性之 解构
  6. goroutine,channel
  7. python自带编译器在写入文件时闪退,或者一步步执行到写入时提示8170。解决办法:
  8. SSH框架之Hibernate第三篇
  9. javaWeb核心技术第十三篇之Ajax
  10. IDEA 2019.2版本下载安装与PJ教程