DRF--重写views
前戏
在前面几篇文章里,我们写了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的更多相关文章
- drf
跨域同源 django做跨域同源 需要把csrf去掉 跨站请求伪造 同源 同源机制:域名.协议.端口号相同的同源 简单请求 不写头部请求 跨域会拦截报错缺少请求信息 (1) 请求方法是以下三种方法之一 ...
- DRF的APIView、GenericAPIView、GenericViewSet的原理分析
一.层次结构 GenericViewSet(ViewSetMixin, generics.GenericAPIView) ---DRF GenericAPIView(views.APIView) -- ...
- DRF的请求响应组件
目录 DRF的请求响应组件 请求模块(request) 概念 request源码简单分析 响应模块(response) 概念 使用方法 response源码简单分析: 解析模块(parse) 概念 使 ...
- drf请求、响应与视图
目录 一.请求 1 定义 2 常用属性 1).data 2).query_params 二.响应 1 Response 2 构造方法 3 状态码 1)信息告知 - 1xx 2)成功 - 2xx 3)重 ...
- DRF教程2-请求和响应
Request objects REST framework中有一个Request对象,是HttpRequest的扩展,提供了新的请求解析,Request的核心功能就是request.data,它和r ...
- DRF 有无外键操作实例
models.py from django.db import models # Create your models here. class Category(models.Model): &quo ...
- drf基础
1.什么是编程? 数据结构和算法的结合 2.什么是REST? 同一个功能会产生五花八门的url(把查看单条记录和查看多条记录都看成是一个功能),而且响应回去的数据也没有同一的格式规范,这就造成了前后端 ...
- drf序列化高级、自定义只读只写、序列化覆盖字段、二次封装Response、数据库查询优化(断关联)、十大接口、视图家族
目录 自定义只读 自定义只写 序列化覆盖字段 二次封装Response 数据库关系分析 断外键关联关系 ORM操作外键关系 ORM四种关联关系 基表 系列化类其他配置(了解) 十大接口 BaseSer ...
- Drf(DjangoRestFramewok)
第一部分 问题 1.前后端分离? vue.js 后端给前段返回json数据 2.移动端盛行. app 后端给app返回json数据 3.PC端应用? crm项目,前段后端一起写,运行在浏览器上. 一般 ...
- 02 drf源码剖析之快速了解drf
02 drf源码剖析之快速了解drf 目录 02 drf源码剖析之快速了解drf 1. 什么是drf 2. 安装 3. 使用 3. DRF的应用场景 1. 什么是drf drf是一个基于django开 ...
随机推荐
- Ligg.EasyWinApp-102-Ligg.EasyWinForm:Function--ControlBox、Tray、Resize、Menu
首先请在VS里打开下面的文件,我们将对源码分段进行说明: Function(功能):一个应用的功能界面,一个应用对应多个Function(功能):如某应用可分为管理员界面.用户界面. 首先我们来看一下 ...
- Java_JDBC 连接
今天,接着上一篇( mysql 数据库 )的基础上,我就写一下 Java 怎样连接数据库,并且操作数据库. 首先,我们先来准备一下数据库连接的驱动: mysql 的 jar 包下载地址:https:/ ...
- vue中监听路由参数的变化
在vue项目中,假使我们在同一个路由下,只是改变路由后面的参数值,期望达到数据的更新. mounted: () =>{ this.id = this.$route.query.id; this. ...
- Cesium专栏-气象要素(温度、降水)色斑图制作
Cesium Cesium 是一款面向三维地球和地图的,世界级的JavaScript开源产品.它提供了基于JavaScript语言的开发包,方便用户快速搭建一款零插件的虚拟地球Web应用,并在性能,精 ...
- 从0系统学Android--3.6 RecyclerView
从0系统学Android--更强大的滚动控件---RecyclerView 本系列文章目录:更多精品文章分类 本系列持续更新中.... 参考<第一行代码> 首先说明一点昨天发了一篇关于 L ...
- mongodb使用_遍历列表中的元素,作为变量,循环修改mongodb中的字段
一.问题描述: 需要将工作界面上的一些已经离职的用户状态改为失效,并备注为离职 二.需要准备/拿到手的工具/条件/数据: 1.已离职人员名单(excel格式) 2.任意mongodb工具(笔者使用的是 ...
- tensorflow 神经网络模型概览;熟悉Eager 模式;
典型神经网络模型:(图片来源:https://github.com/madalinabuzau/tensorflow-eager-tutorials) 保持更新,更多内容请关注 cnblogs.com ...
- OAuthon2.0机制详解
最近在忙企业微信和钉钉的第三方应用开发,需要获取一些信息,第一个就是这个OAuthon2.0,先详细了解下概念和流程 一.应用场景 我们要想用第三方播放器播放你的云盘账号里面的一些秘密视频资源,为了要 ...
- dnf & yum
CentOS8 配置软件源 在 CentOS8 中.使用了基于DNF技术(YUM v4)的 YUM 工具. YUM v4 与之前在 CentOS7 上使用的 YUM v3 相比具有以下优点: 提高性能 ...
- Python 获取MD5加密值
Python 获取MD5加密值方法封装 import hashlib def get_md5(s): """获取MD5加密值 :param s: 需要加密的字符串 :re ...