前戏

在前面几篇文章里,我们写了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. C#NULL条件运算符

    C#6.0新增的特性 NULL条件运算符 ?. 之前我们在需要判断某个对象是否为空的是这样的 Person per = null; if (per != null) { Console.Write(& ...

  2. 从零开始搭建solo个人博客系统

    目录 1.博客系统的搭建流程 2.服务器选购 2.1阿里云学生主机 2.2普通云主机 3.域名购买与备案(可选) 3.1域名购买 3.2域名服务器备案 3.3域名服务器解析 4.solo安装 4.1 ...

  3. LeetCode刷题191122

    博主渣渣一枚,刷刷leetcode给自己瞅瞅,大神们由更好方法还望不吝赐教.题目及解法来自于力扣(LeetCode),传送门. 算法: 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度. ...

  4. Windows自动执行应用程序或脚本(可以通过写bat文件定时关机等)

    1. Windows每天定时执行某个应用程序 1.1 右键我的电脑选择管理,并选择任务计划程序,如下 演示 --- 1.2 创建基本任务 演示 1.3 Windows每天定时关机设置参数 演示 1. ...

  5. Java程序远程无法执行nohup命令

    问题的上下文: 由于生产无法使用 jenkins 发布,所以采用 ch.ethz.ssh2 或叫 ganymed-ssh2 的开源 java 的 ssh api 进行远程发布. 在发起重启时,远程执行 ...

  6. C++设计考试例题

    1. 采用面向对象的方式编写一个通迅录管理程序,通迅录中的信息包括:姓名,公司,联系电话,邮编.要求的操作有:添加一个联系人,列表显示所有联系人.先给出类定义,然后给出类实现.(提示:可以设计二个类, ...

  7. SpringCloud(六):服务网关zuul-API网关(服务降级和过滤)

    什么是API网关: 在微服务架构中,通常会有多个服务提供者.设想一个电商系统,可能会有商品.订单.支付.用户等多个类型的服务,而每个类型的服务数量也会随着整个系统体量的增大也会随之增长和变更.作为UI ...

  8. C# 新特性 操作符单?与??和 ?. 的使用

    1.单问号(?) 1.1 单问号运算符可以表示:可为Null类型,C#2.0里面实现了Nullable数据类型 //A.比如下面一句,直接定义int为null是错误的,错误提示为无法将null转化成i ...

  9. 利用socket传递图片

    package com.company.s3; import java.io.File; import java.io.FileOutputStream; import java.io.InputSt ...

  10. javascript 模块化开发(一)

    什么是模块化 将一组模块(及其依赖项)以正确的顺序拼接到一个文件(或一组文件)中的过程. 传统的模块化做法. 模块是实现特定功能的一组属性和方法的封装. 将模块写成一个对象,所有的模块成员都放到这个对 ...