前戏

在前面几篇文章里,我们写了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,否则会报错

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

DRF--重写views的更多相关文章

  1. drf

    跨域同源 django做跨域同源 需要把csrf去掉 跨站请求伪造 同源 同源机制:域名.协议.端口号相同的同源 简单请求 不写头部请求 跨域会拦截报错缺少请求信息 (1) 请求方法是以下三种方法之一 ...

  2. DRF的APIView、GenericAPIView、GenericViewSet的原理分析

    一.层次结构 GenericViewSet(ViewSetMixin, generics.GenericAPIView) ---DRF GenericAPIView(views.APIView) -- ...

  3. DRF的请求响应组件

    目录 DRF的请求响应组件 请求模块(request) 概念 request源码简单分析 响应模块(response) 概念 使用方法 response源码简单分析: 解析模块(parse) 概念 使 ...

  4. drf请求、响应与视图

    目录 一.请求 1 定义 2 常用属性 1).data 2).query_params 二.响应 1 Response 2 构造方法 3 状态码 1)信息告知 - 1xx 2)成功 - 2xx 3)重 ...

  5. DRF教程2-请求和响应

    Request objects REST framework中有一个Request对象,是HttpRequest的扩展,提供了新的请求解析,Request的核心功能就是request.data,它和r ...

  6. DRF 有无外键操作实例

    models.py from django.db import models # Create your models here. class Category(models.Model): &quo ...

  7. drf基础

    1.什么是编程? 数据结构和算法的结合 2.什么是REST? 同一个功能会产生五花八门的url(把查看单条记录和查看多条记录都看成是一个功能),而且响应回去的数据也没有同一的格式规范,这就造成了前后端 ...

  8. drf序列化高级、自定义只读只写、序列化覆盖字段、二次封装Response、数据库查询优化(断关联)、十大接口、视图家族

    目录 自定义只读 自定义只写 序列化覆盖字段 二次封装Response 数据库关系分析 断外键关联关系 ORM操作外键关系 ORM四种关联关系 基表 系列化类其他配置(了解) 十大接口 BaseSer ...

  9. Drf(DjangoRestFramewok)

    第一部分 问题 1.前后端分离? vue.js 后端给前段返回json数据 2.移动端盛行. app 后端给app返回json数据 3.PC端应用? crm项目,前段后端一起写,运行在浏览器上. 一般 ...

  10. 02 drf源码剖析之快速了解drf

    02 drf源码剖析之快速了解drf 目录 02 drf源码剖析之快速了解drf 1. 什么是drf 2. 安装 3. 使用 3. DRF的应用场景 1. 什么是drf drf是一个基于django开 ...

随机推荐

  1. subprocess 的 Popen用法

    使用Popen方法时,需要获取输出内容时可以按如下方法获取: # -*- coding:utf-8 -*- import subprocess cmd = r"ping www.baidu. ...

  2. java 后台上传文件

    java 后台上传文件 public static String uploadFile(File file, String RequestURL) throws IOException { Strin ...

  3. linux用户组相关,密码相关,文件操作,和启动级别

    一.开机重启和用户切换 注意,当关机或重启前,都应当先执行一下sync指令,把内存的数据写入磁盘,防止数据丢失. shutdown命令 shutdown -h now :立即关机 shutdown - ...

  4. CentOS 7怎么删除mariadb

    参考链接:https://www.cnblogs.com/ytkah/p/10876824.html

  5. 多个div的多文本部分展开显示+关键字自动标注

    效果: 源码: <%@ page language="java" contentType="text/html; charset=utf-8" pageE ...

  6. RabbitMQ几个常用面试题

    以下观点,仅为个人理解的总结,如有错漏,欢迎指正! -------------------------------------------------------------------------- ...

  7. Redis分布式缓存实现

    基于redis分布式缓存实现 第一:Redis是什么? Redis是基于内存.可持久化的日志型.Key-Value数据库高性能存储系统,并提供多种语言的API. 第二:出现背景 数据结构(Data S ...

  8. 【GUI】基于V7开发板的裸机和各种RTOS版本的emWin程序模板,支持硬件JPEG,已发布(2019-05-26)

    说明: 1.MDK请使用5.26及其以上版本,IAR请使用8.30及其以上版本. 2.修正了ST提供的部分驱动设计不合理的地方. 3.原创实现硬件JPEG添加到emWin中,实现简单,全程使用SDRA ...

  9. 利用Python进行数据分析-Pandas(第三部分)

    访问数据是使用本书所介绍的这些工具的第一步.这里会着重介绍pandas的数据输入与输出,虽然别的库中也有不少以此为目的的工具. 输入输出通常可以划分为几个大类:读取文本文件和其他更高效的磁盘存储格式, ...

  10. Web前端基础(16):jQuery基础(三)

    1. jQuery动画效果 jQuery提供的一组网页中常见的动画效果,这些动画是标准的.有规律的效果:同时还提供给我们了自定义动画的功能. 1.1 显示动画 方式一: $("div&quo ...