前戏

在前面几篇文章里,我们写了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. UML简单介绍—类图这么看就懂了

    如何看懂类图 1.类图简介 描述类的内部结构和类与类之间的关系,是一种静态结构图. 在UML类图中,常见的有以下几种关系: 泛化(Generalization),  实现(Realization),关 ...

  2. [css flex布局]实例一,本来还想挺简单的,弄了挺久呢,先写一部分

    全是代码,直接拷走吧,看是不怎么好看的 参考:http://www.ruanyifeng.com/blog/search.html?cx=016304377626642577906%3Ab_e9ska ...

  3. KVO-键值监听

    键值监听,就是可以监听对象某个属性值的变化: 首先,在工程中,新建一个Person的类 @interface Person : NSObject @property (nonatomic, copy) ...

  4. Zabbix自定义监控项(模板)

    虽然Zabbix提供了很多的模板(简单理解为监控项的集合),在zabbix界面点击share按钮就可以直接跳到模板大全的官方网站,但是由于模板内的监控项数量太多不好梳理且各种模板质量参差不齐,还是建议 ...

  5. C# List、Array、Dictionary之间相互转换

    Array转换为List List转换为Array Array转Dictionary Dictionary转Array List转Dictionary Dictionary转List IQueryab ...

  6. 【转载】Vue.js 安装及其环境搭建

    注:最近在学习Vue,以下是环境搭配方法: ****************************************************************************** ...

  7. Shell—脚本编程进阶

    shell脚本进阶之条件语句 条件选择if语句 https://www.runoob.com/?s=shell&page=1 https://www.cnblogs.com/flylinux/ ...

  8. Java的反射机理

    Java反射是一种间接操作目标对象的机制,核心是JVM在运行的时候才动态加载类,并且对于任意一个类,都能够知道这个类的全部属性和方法等,调用方法以及访问属性,而且不需要提前在编译期知道运行的对象是什么 ...

  9. Java之Math类

    概述 java.lang.Math 类包含用于执行基本数学运算的方法,如初等指数.对数.平方根和三角函数.类似这样的工具 类,其所有方法均为静态方法,并且不会创建对象,调用起来非常简单. 基本运算的方 ...

  10. [CodeForces-1225B] TV Subscriptions 【贪心】【尺取法】

    [CodeForces-1225B] TV Subscriptions [贪心][尺取法] 标签: 题解 codeforces题解 尺取法 题目描述 Time limit 2000 ms Memory ...