DRF之视图组件
不断的优化我们写的程序,是每个程序员必备的技能和职业素养,也是帮助我们成长的非常重要的手段。
使用serializer进行put接口设计
根据规范,PUT接口用来定义用户对数据修改的逻辑,也就是update,它的url是这样的, 127.0.0.1/books/1/,请求方式是PUT,1代表的是具体的数据,使用户动态传递的,所以我们的url应该是这样的:
re_path(r'books/(\d+)/$', views.BookFilterView.as_view()),
此时我们应该重新定义一个视图类,因为url不一样了,所以在views.py中,需新增一个视图类:
from rest_framework.views import APIView
from app_serializer import BookSerializer class BookFilterView(APIView): def put(self, request, nid):
book_obj = Book.objects.get(pk=nid) serialized_data = BookSerializer(data=request.data, instance=book_obj) if serialized_data.is_valid():
serialized_data.save()
else:
return Response(serialized_data.errors)
请注意,在序列化时,我们除了传入data参数外,还需告诉序列化组件,我们需要更新哪条数据,也就是instance,另外,我们使用的序列化类还是之前那个:
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book fields = ('title',
'price',
'publish',
'authors',
'author_list',
'publish_name',
'publish_city'
)
extra_kwargs = {
'publish': {'write_only': True},
'authors': {'write_only': True}
} publish_name = serializers.CharField(max_length=32, read_only=True, source='publish.name')
publish_city = serializers.CharField(max_length=32, read_only=True, source='publish.city') author_list = serializers.SerializerMethodField() def get_author_list(self, book_obj):
# 拿到queryset开始循环 [{}, {}, {}, {}]
authors = list() for author in book_obj.authors.all():
authors.append(author.name) return authors
使用POSTMAN工具发送一个PUT请求修改数据: PUT http://127.0.0.1:9001/serializers/books/1。
请注意,此时会报错:
RuntimeError: You called this URL via PUT, but the URL doesn’t end in a slash and you have APPEND_SLASH set. Django can’t redirect to the slash URL while maintaining PUT data. Change your form to point to 127.0.0.1:9001/serializers/books/1/ (note the trailing slash), or set APPEND_SLASH=False in your Django settings.
因为,如果是GET请求,Django的全局APPEND_SLASH参数为True,所以会在url后面加上/(如果没有),但是如果是PUT或者DELETE请求,APPEND_SLASH不会添加 / 到url末尾。而我们上面定义的url是明确以 / 结尾的,所以,我们应该在url后面加上反斜线 / ,或者把url修改为不以斜线结尾。
加上之后,再次发送请求修改数据:PUT http://127.0.0.1:9001/serializers/books/1/,查看数据库,发现,数据已经被修改了。
这就是PUT接口逻辑的设计,分为如下几个步骤:
- url设计:re_path(r’books/(\d+)/$’, views.BookFilterView.as_view())
- 视图类:重新定义一个视图类
- put方法:在视图类中定义一个put方法
- 序列化:在序列化的过程中,需要传入当前修改的数据行,参数名为instance
- 序列化类:不需要修改
- url路径:请求时,发送的url必须与urls.py中定义的url完全匹配
使用serializer进行delete接口设计
接下来,继续设计delete接口,根据规范,delete接口的url为:127.0.0.1/books/1/,请求方式是DELETE,与put是一致的,都是对用户指定的某行数据进行操作,数字1是动态的,所以我们的url不变:
re_path(r'books/(\d+)/$', views.BookFilterView.as_view()),
同样的,视图类和序列化类都不需要重新定义,只需要在视图类中定义一个delete方法即可,如下代码所示:
class BookFilterView(APIView):
def delete(self, request, nid):
book_obj = Book.objects.get(pk=nid).delete()
return Response("")
def put(self, request, nid):
book_obj = Book.objects.get(pk=nid)
serialized_data = BookSerializer(data=request.data, instance=book_obj)
if serialized_data.is_valid():
serialized_data.save()
return Response(serialized_data.data)
else:
return Response(serialized_data.errors)
用POSTMAN来试试DELETE请求:http://127.0.0.1:9001/serializers/books/53/,我们将刚刚添加的数据删除,操作成功,同样的,请注意,请求url必须完全匹配urls.py中定义的url。
使用serializer进行单条数据的接口设计
最后一个接口的设计,是对单条数据进行获取,根据规范url为:127.0.0.1/books/1/,请求方式为GET,根据url和前面两个接口的经验,这次仍然使用之前的视图类,因为根据REST规范,url唯一定位资源,127.0.0.1/books/1/和127.0.0.1/books/是不同的资源,所以,我们不能使用之前那个获取全部数据的视图类。这里肯定不能重用之前的那个get方法,必须重新定义一个get方法。
urls.py不变,新增三个接口逻辑后的视图类如下:
class BookFilterView(APIView):
def get(self, request, nid):
book_obj = Book.objects.get(pk=nid) serialized_data = BookSerializer(book_obj, many=False) return Response(serialized_data.data) def delete(self, request, nid):
book_obj = Book.objects.get(pk=nid).delete() return Response("") def put(self, request, nid):
book_obj = Book.objects.get(pk=nid) serialized_data = BookSerializer(data=request.data, instance=book_obj) if serialized_data.is_valid():
serialized_data.save()
return Response(serialized_data.data)
else:
return Response(serialized_data.errors)
many=False, 当然,也可以不传这个参数,因为默认是False。通过POSTMAN发送请求,成功。三个接口定义完成了,加上上一节课的get和post,两个视图类的接口逻辑如下:
class BookView(APIView):
def get(self, request):
origin_books = Book.objects.all()
serialized_books = BookSerializer(origin_books, many=True) return Response(serialized_books.data) def post(self, request):
verified_data = BookSerializer(data=request.data) if verified_data.is_valid():
book = verified_data.save()
return Response(verified_data.data)
else:
return Response(verified_data.errors) class BookFilterView(APIView):
def get(self, request, nid):
book_obj = Book.objects.get(pk=nid) serialized_data = BookSerializer(book_obj, many=False) return Response(serialized_data.data) def delete(self, request, nid):
book_obj = Book.objects.get(pk=nid).delete() return Response("") def put(self, request, nid):
book_obj = Book.objects.get(pk=nid) serialized_data = BookSerializer(data=request.data, instance=book_obj) if serialized_data.is_valid():
serialized_data.save()
return Response(serialized_data.data)
else:
return Response(serialized_data.errors)
到此为止,我们已经通过序列化组件设计出了符合REST规范的五个常用接口,已经足够优秀了,但是还不够完美,现在假设,我们有多个数据接口,比如(Book,Author,Publish…..)等数据表都需要定义类似的接口,可以预见,我们需要重复定义类似上面的五个接口,这种方式将会导致大量的重复代码出现,显然,我们的程序还有很多可以优化的地方。
好了,结合刚刚上课是给大家回顾的混合类和多继承,我们是否可以使用下面的方式呢?
class GetAllData():
def get(self, request):pass class GetOneData():
def get(self, request, nid):pass class DeleteOneData():
def delete(self, request, nid):pass class UpdateOneData():
def put(self, request, nid):pass class CreateData():
def post(self, request):pass class BookView(APIView, GetAllData, CreateData):pass class BookFilterView(APIView, GetOneData, DeleteOneData, UpdateOneData):pass
接下来,我们一起来看看DRF是如何做的,其他,它的解决方式与我们上面的方式的思路是一样的。将每个接口都写到独立的类中,然后使用多继承,或者成为mixin的这种方式,就可以对我们的程序进行优化,mixin的方式非常常见,在学网络编程的时候,如果你看过socketserver源码,就会发现,socketserver中就有对mixin的实现,即,假设我们需要进程的时候,我们继承进程类,如果我们需要线程的时候,我们继承线程类即可。
使用mixin优化接口逻辑
mixin的使用方式介绍
urls.py有些区别:
from django.urls import re_path from mixiner import views urlpatterns = [
re_path(r'books/$', views.BookView.as_view()),
re_path(r'books/(?P<pk>\d+)/$', views.BookFilterView.as_view()),
]
views.py
# -*- coding: utf-8 -*-
from rest_framework.mixins import (
ListModelMixin,
CreateModelMixin,
UpdateModelMixin,
DestroyModelMixin,
RetrieveModelMixin
)
from rest_framework.generics import GenericAPIView # 当前app中的模块
from .models import Book
from mixin_serializer import BookSerializer # Create your views here. class BookView(ListModelMixin, CreateModelMixin, GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs) class BookFilterView(DestroyModelMixin,
UpdateModelMixin,
RetrieveModelMixin,
GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs) def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs) def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
DRF将这五个接口定义在不同的ModelMixin中,使用步骤如下:
- 导入ModelMixin
- 视图类继承所需的ModelMix
- 不再继承APIView,需要继承generics.GenericAPIView
- 必须包含两个类变量:queryset,serializer_class
- 接口中不需要定义任何逻辑操作,一切交给mixin
- 每个接口的返回值不同,对应关系:{“get”: “list”, “delete”: “destroy”, “put”: “update”, “get”: “retrieve”, “post”: “create”}
其中有两个get方法,但是分属于不同的视图类,请注意在url中的不同点,因为我们统一给的都是QuerySet,所以,需要通过传入一个名为pk的命名参数,告诉视图组件,用户需要操作的具体数据。
mixin源码剖析
到底它是如何做的呢?我们来简单剖析一下源码:
- Django程序启动,开始初始化,读取urls.py, 读取settings, 读取视图类
- 执行as_views(), BookView没有,需要到父类中找
- 几个ModelMixin也没有,GenericAPIView中没有,继续到GenericAPIView(APIView)中找
- 找到了,并且与之前的逻辑是一样的,同时我们发现GenericAPIView中定义了查找queryset和serializer_class类的方法
- as_view()方法返回重新封装的视图函数,开始建立url和视图函数之间的映射关系
- 等待用户请求
- 接收到用户请求,根据url找到视图函数
- 执行视图函数的dispatch方法(因为视图函数的返回值是:return self.dispatch()
- dispatch分发请求,查找到视图类的五个方法中的某个
- 开始执行,比如post请求,返回:self.create(),视图类本身没有,则会到父类中查找
- 最后在CreateModelMixin中查找
- 执行create()方法,获取queryset和serializer_class
- 返回数据
在对单条数据进行操作的几个方法里面,比如retrieve,会执行get_object()方法,该方法会根据lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field来查找操作对象,我们可以通过修改self.lookup_url_kwarg变量名来自定义参数。
好了,以上就是mixin的源码剖析。
使用view优化接口逻辑
view的使用方式介绍
看似已经优化的非常完美了,但是,在一个对性能要求极高的项目里面,我们的程序还可以继续优化,还需要继续优化,不断的优化我们写的程序,是每个程序员必备的技能,也是帮助我们成长的非常重要的手段。同样的思路,同样的方式,我们可以将多个接口封装到一个功能类中,请看下面的代码:
使用viewset优化接口逻辑
这样就结束了吗?哈哈哈,还是那句话,看似已经优化的非常完美了,但是,在一个对性能要求极高的项目里面,我们的程序还可以继续优化,还需要继续优化,不断的优化我们写的程序,是每个程序员必备的技能,也是帮助我们成长的非常重要的手段。
viewset的使用方式介绍
urls.py有变化哦:
from django.urls import re_path from viewsetter import views urlpatterns = [
re_path(r'books/$', views.BookView.as_view({
'get': 'list',
'post': 'create'
})),
re_path(r'books/(?P<pk>\d+)/$', views.BookView.as_view({
'get': 'retrieve',
'put': 'update',
'delete': 'destroy'
})),
]
使用方式非常简单,接下来,我们直接看源码。我们给as_view()方法传递了参数,这就是最神奇的地方。
# -*- coding: utf-8 -*-
# django rest framework组件
from rest_framework.viewsets import ModelViewSet # 当前app中的模块
from .models import Book
from .serializer_classes import BookSerializer # Create your views here. class BookView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
使用方式非常简单,接下来,我们直接看源码
viewset源码剖析
- Django程序启动,开始初始化,读取urls.py, 读取settings, 读取视图类
- 执行as_views(), BookView没有,需要到父类(ModelViewSet)中找
- ModelViewSet继承了mixins的几个ModelMixin和GenericViewSet,显然ModelMixin也没有,只有GenericViewSet中有
- GenericViewSet没有任何代码,只继承了ViewSetMixin和generics.GenericAPIView(这个我们已经认识了)
- 继续去ViewSetMixin中查找,找到了as_view类方法,在重新封装view函数的过程中,有一个self.action_map = actions
- 这个actions就是我们给as_view()传递的参数
- 绑定url和视图函数(actions)之间的映射关系
- 等待用户请求
- 接收到用户请求,根据url找到视图函数
- 执行视图函数的dispatch方法(因为视图函数的返回值是:return self.dispatch()
- dispatch分发请求,查找到视图类的五个方法中的某个
- 开始执行,比如post请求,返回:self.create(),视图类本身没有,则会到父类中查找
- 最后在CreateModelMixin中查找
- 执行create()方法,获取queryset和serializer_class
- 返回数据
这就是viewset的优化方案,整个优化方案最重要的地方就是urls.py中我们传入的参数,然后对参数进行映射关系绑定。
DRF之视图组件的更多相关文章
- DRF的视图组件
目录 DRF的视图组件 两大视图类 六大视图工具类 九大工具视图类 两大视图集基类 DRF的视图组件 DRF的视图组件大概分以下几类 两大视图类 APIView.GenericAPIView from ...
- DRF之视图组件 三次封装
1.为什么要进行封装 1.1 在处理表的时候,如果有几十张表都需要增删改查查时,如果每一张表都写这些方法,会让代码显得冗余,所以需要将这些方法进行封装,然后不同的表都去继承这写方法.(这是思路) 1. ...
- DRF(5) - 频率组件、url注册器、响应器、分页器
一.频率组件 1.使用DRF简单频率控制实现对用户进行访问频率控制 1)导入模块,定义频率类并继承SimpleRateThrottle # 导入模块 from rest_framework.throt ...
- drf视图组件、认证组件
视图组件 1.基本视图 url(r'^publish/$', views.PublishView.as_view()), url(r'^publish/(?P<pk>\d+)/$', vi ...
- DRF 视图组件,路由组件
视图组件 -- 第一次封装 -- GenericAPIView(APIView): queryset = None serializer_class = None def ge ...
- 【DRF框架】视图组件
基于mixins视图类 from rest_framework import mixins # 创建视图 class CreateModelMixin(object) def create(self, ...
- DRF视图组件
DRF视图组件: CVB模式继承----五层 from django.views import View # Django的View from rest_framework.views import ...
- DRF 视图组件
目录 DRF 视图组件 视图组件大纲 两个视图基本类 五个扩展类 九个子类视图 视图集 常用视图集父类 魔法类 一览表 DRF中视图的"七十二变" 第一层:基于APIview的五个 ...
- DRF(3) - 序列化组件(GET/PUT/DELETE接口设计)、视图优化组件
一.序列化组件 基于上篇随笔的表结构,通过序列化组件的ModelSerializer设计如下三个接口: GET 127.0.0.1:8000/books/{id} # 获取一条数据,返回值:{} PU ...
随机推荐
- day18-事务与连接池 4.事务特性
- 详解 javascript中offsetleft属性的用法(转)
详解 javascript中offsetleft属性的用法 转载 2015-11-11 投稿:mrr 我要评论 本章节通过代码实例介绍一下offsetleft属性的用法,需要的朋友可以做一 ...
- Luogu 2668 [NOIP2015]斗地主
打牌技术不精,没有把$A$放在顺子里面搜,WA了好长时间. 盗用大佬的一张图: 当时自己搜的时候没有把四张牌拆成三带一等情况. 然后还有一点就是四张三张都出完之后直接数一数剩下的一张两张牌还要多少次出 ...
- Qt Console Application
代码编译完生成的.o文件(又称对象文件,是可执行文件)和链接.o文件形成的.exe可执行文件都保存在“build-Project-Desktop_Qt_5_8_0_GCC_64bit-Debug”中. ...
- JavaScript学习系列8 - JavaScript中的关系运算符
JavaScript中有8个关系运算符,分别是 ===, !===, ==, !=, <, <=, >, >= 1. 恒等运算符 (===) ===也叫做 严格相等运算符,它要 ...
- bootstrap常用部件下载
http://shapebootstrap.net/item/1524915-adminlte-dashboard-and-control-panel/live-demo
- iphone5 A1429国行IOS8.4.1 越狱 完美使用电信3G
国航 8.4.1,越狱,使用电信3G,能打电话,能发短信,正常上网使.(越狱降级方式,请参照本文末端连接) 有好多人说不管怎么试都是无服务,请查看 设置-蜂窝移动网络-漫游里面,必须只有 语音漫游 这 ...
- 2018ICPC徐州区域赛网络赛B(逆序枚举或者正序深度搜索)
#include<bits/stdc++.h>using namespace std;int n,m,k,l;int x[1007],y[1007],z[1007];int dp[1007 ...
- nginx 安装遇到的问题
今天想学学 nginx,于是先把它安装起来.按照 http://nginx.org/en/linux_packages.html 上面的方法,在我的 ubuntu 虚拟机上很容易地就安装好了.可是要运 ...
- SQL——模糊查询
前言 在这个大数据时代,我们都离不开对数据的增删改查,增加.删除.修改这些看似都是一步完成的事情,但是对于查询来说,好的查询SQL可以大大的减少系统内存运行时间,提高系统的反应速度.这里简单的介绍一下 ...