Restful 1 -- REST、DRF(View源码解读、APIView源码解读)及框架实现
一、REST
1、什么是编程?
数据结构和算法的结合
2、什么是REST?
- url用来唯一定位资源,http请求方式来区分用户行为
首先回顾我们曾经做过的图书管理系统,我们是这样设计url的,如下:
127.0.0.1:9001/books/
127.0.0.1:9001/get_all_books/ 访问所有的数据 127.0.0.1:9001/books/{id}/
127.0.0.1:9001/books/{id}?method=get 访问单条数据 127.0.0.1:9001/books/add/
127.0.0.1:9001/books/?type=create 创建数据 127.0.0.1:9001/books/delete/ 127.0.0.1:9001/books/update/
分析:以上定义的url虽然也可以实现功能,但是因个人命名习惯等的不同,同一个功能会产生五花八门url,而且响应回去的数据(包括错误提示等)格式也没有统一的规范,这就造成了前后端交互上的困难。
因此,就产生了REST,REST的url唯一代表资源,HTTP请求方式来区分用户行为,下面就是符合REST的url设计规范的示例:
url的设计规范:
GET: 127.0.0.1:9001/books/ # 获取所有数据
GET: 127.0.0.1:9001/books/{id} # 获取单条数据
POST: 127.0.0.1:9001/books/ # 增加数据
DELETE: 127.0.0.1:9001/books/{id} # 删除数据
PUT: 127.0.0.1:9001/books/{id} # 修改数据
数据响应规范:
GET: 127.0.0.1:9001/books/ # 返回[{}, {}, {}]
GET: 127.0.0.1:9001/books/{id} # 返回单条数据{}
POST: 127.0.0.1:9001/books/ # 返回添加成功的数据{}
DELETE: 127.0.0.1:9001/books/{id} # 返回空""
PUT: 127.0.0.1:9001/books/{id} # 返回{} ,更新后完整的一条记录,注意并非一个字段
错误处理:
{ "error": "error_message" }
REST是只用软件架构设计风格,不是标准,也不是技术实现,只是提供了一组设计原则和约束条件,是目前最流行的API设计规范,用于web数据接口的设计。2000年,由Roy Fielding在他的博士论文中提出,Roy Fielding是HTTP规范的主要编写者之一。
那么,我们接下来要学的Django Rest Framework与REST有什么关系呢?
其实,DRF(Django REST Framework)是一套基于django开发的,帮助我们更好的设计符合REST规范的web应用的一个Django App,所以,本质上,它是要给Django App。
二、知识储备
学习新知识之前,先回顾以下几个知识点:
1、CBV(class based view)
django.views import View
class LoginView(View):
def get(self, request):
pass
def post(self, request):
pass
2、 类方法classmethod & classonlymethod
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age # 注意:Person类加载时,会执行装饰器函数classmethod(sleepping),并将结果赋给slepping
@classmethod # 相当于 sleepping = classmethod(sleepping)
def sleepping(cls):
print("Jihong is sleepping") @classonlymethod
def guangjie(cls):
print("Jihong is shopping") Person.sleepping() # 类直接调用类方法
Person.guangjie() # 类直接调用方法
jihong = Person("jihong", 20)
jihong.sleepping() # 对象可以调用类方法
jihong.guangjie() # 报错,对象不能调用由@classonlymethod装饰的方法
总结:
@classmethod装饰(python加的装饰器)的方法可以由对象和类调用;
@classonlymethod装饰(django加的装饰器)只能由类直接调用
3、反射
getattr , hasattr , setattr
4、self定位
明确 slef 指向谁 - 始终指向调用者
5、http请求协议
协议就是沟通双方约定俗成的规范,即解析数据的规则
6、form表单的enctype属性的有三种请求协议
如果通过form表单提交用户数据,可以使用form表单的enctype属性来定义数据编码协议,该属性有三个值,代表三种数据编码协议:
- application/x-www-form-urlencoded:使用&符号连接多个键值对,键值对用等号拼接,默认;
- multipart/form-data:上传文件、图片时使用该方式;
7、JavaScript中object,例如:{name:"pizza"} <==>json的相互转换方式
JSON.stringfy(data) =相当于=》 python json.dumps()
JSON.parse(data) =相当于=》 pthon json.loads()
注意:
dumps是将dict转化成str格式,loads是将str转化成dict格式
三、Django REST Framework(DRF序列化)
为什么使用DRF?
从概念可以看出,有了这样的一个App,能够帮助我们更好的设计符合RESTful规范的web应用,实际上,没有它,我们也能自己设计符合规范的web应用,如下代码中,我们就手动实现了符合RESTful规范的web应用:
class CourseView(View):
def get(self, request):
course_list = list() for course in Course.objects.all():
course = {
"course_name": course.course_name,
"description": course.description
} course_list.append(course) return HttpResponse(json.dumps(course_list, ensure_ascii=False))
如上代码所示,我们获取所有的课程数据,并根据REST规范,将所有资源通过列表返回给用户,可见,就算没有DRF,我们也能设计出符合RESTful规范的接口,甚至是整个App应用,但是,如果所有的接口都自定义,难免会出现重复代码,为了提高工作效率,我们建议使用优秀的工具,DRF就是这样一个优秀的工具,另外,它不仅能够帮助我们快速的设计符合REST规范的接口,还提供诸如认证、权限等等其他强大功能。
什么时候使用DRF?
前边提到,REST是目前最流行的API设计规范,如果使用Django开发你的web应用,那么请尽量使用DRF,如果使用的是Flask,可以使用Flask-RESRful。
DRF官方文档中有详细介绍,使用如下命令安装,首先安装Django,然后安装DRF:
1
2
|
>>> pip install django # 安装django,已经安装的可以直接下载DRF >>> pip install djangorestframework # 下载 |
安装完成之后,我们就可以开始使用DRF框架来实现我们的web应用了,这部分内容包括如下知识点:
- APIView
- 解析器组件
- 序列化组件
- 视图组件
- 认证组件
- 权限组件
- 频率控制组件
- 分页组件
- 相应器组件
- url控制器
介绍DRF,必须介绍APIView,它是重中之重,是下面所有组件的基础,因为所有的请求都是通过它来分发的,至于它究竟是如何分发请求的?想要弄明白这个问题,就必须解读它的源码,而想要解读DRF APIView的源码,就必须先解读django中views.View类的源码,为什么使用视图类调用as_view()之后,请求就可以被不同的函数处理?
1、回顾CBV,View源码解读
# views.py中代码如下:
from django.views import View
class LoginView(View):
def get(self, request):
pass
def post(self, request):
pass # urls.py中代码如下:
from django.urls import path, include, re_path
from classbasedView import views
urlpatterns = [
re_path("login/$", views.LoginView.as_view())
]
1)启动django项目:python manage.py runserver 127.0.0.1:8000后
2)开始加载settings配置文件
- 读取models.py
- 加载views.py
- 加载urls.py,执行as_view( ): views.LoginView.as_view()
LoginView中没有as_view,因此去执行父类View中as_view方法,父类View的相关源码如下:
class View:
http_method_names = ['get', 'post', 'put', ...]
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value) @classonlymethod
def as_view(cls, **initkwargs):
for key in initkwargs:
...
def view(request, *args, **kwargs):
# 实例化一个对象,对象名称为self,self是cls的对象,谁调用了as_view
# cls就是谁(当前调用as_view的是LoginView)
# 所以,此时的self就是LoginView的实例化对象
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs update_wrapper(view, cls, updated=())
update_wrapper(view, cls.dispatch, assigned=())
return view def dispatch(self, request, *args, **kwargs):
if request.method.lower() in self.http_method_names:
# 通过getattr找到的属性,已和对象绑定,访问时不需要再指明对象了
# 即不需要再:self.handler,直接handler()执行
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
上面源码中可以看出,as_view是一个类方法,并且方法中定义了view函数,且as_view将view函数返回,此时url与某一个函数的对应关系建立,并开始等待用户请求。
3)当用户发来请求(如get请求),开始执行url对应的view函数,并传入request对象,view函数的执行结果是返回self.dispatch(request, *args, **kwargs)的执行结果,这里的self是指LoginView的实例化对象,LoginView中没有dispatch方法,所以去执行父类View中的dispatch方法,View类中的dispatch函数中通过反射找到self(此时self指LoginView的实例对象)所属类(即LoginView)中的get方法,dispatch函数中的handler(request, *args, **kwargs)表示执行LoginView类中的get方法,其执行结果就是dispatch的执行结果,也就是请求url对应view函数的执行结果,最后将结果返回给用户。
2、APIView
使用:
# views.py中代码:
from rest_framework.views import APIView # 引入APIView
class LoginView(APIView): # LoginView继承APIView
def get(self, request):
pass
def post(self, request):
pass # urls.py中用法同CBV一样,如下示例:
from django.urls import path, include, re_path
from classbasedView import views
urlpatterns = [
re_path("login/$", views.LoginView.as_view())
]
源码解读:
1)启动django项目:python manage.py runserver 127.0.0.1:8000后
2)开始加载settings配置文件
- 读取models.py
- 加载views.py
- 加载urls.py,执行as_view( ): views.LoginView.as_view()
LoginView中没有as_view,因此去执行父类APIView中as_view方法,父类APIView的相关源码如下:
class APIView(View):
...
# api_settings是APISettings类的实例化对象
parser_classes = api_settings.DEFAULT_PARSER_CLASSES
...
settings = api_settings
schema = DefaultSchema() @classmethod
def as_view(cls, **initkwargs): # cls指LoginView
if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
...
# 下面一句表示去执行APIView父类(即View类)中的as_view方法
view = super(APIView, cls).as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs
return csrf_exempt(view) def dispatch(self, request, *args, **kwargs):
...
request = self.initialize_request(request, *args, **kwargs)
...
try:
self.initial(request, *args, **kwargs)
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed response = handler(request, *args, **kwargs) except Exception as exc:
response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
参考View源码解读,我们知道View中的as_view方法返回view函数,此时url与view的对应关系已经建立,等待用户请求。
3)当用户发来请求(如get请求),开始执行url对应的view函数,并传入request对象,View类中view函数的执行结果是返回self.dispatch(request, *args, **kwargs)的执行结果,这里的self是指LoginView的实例化对象,LoginView中没有dispatch方法,所以去执行父类APIView中的dispatch方法,同样,APIView类中的dispatch函数中也是通过反射找到self(此时self指LoginView的实例对象)所属类(即LoginView)中的get方法,dispatch函数中的handler(request, *args, **kwargs)表示执行LoginView类中的get方法,其执行结果就是dispatch的执行结果,也就是请求url对应view函数的执行结果,最后将结果返回给用户。
四、基于Django Rest FrameWork框架的实现
1.基本流程
url.py
from django.conf.urls import url, include
from web.views.s1_api import TestView urlpatterns = [
url(r'^test/', TestView.as_view()),
]
views.py
from rest_framework.views import APIView
from rest_framework.response import Response class TestView(APIView):
def dispatch(self, request, *args, **kwargs):
"""
请求到来之后,都要执行dispatch方法,dispatch方法根据请求方式不同触发 get/post/put等方法 注意:APIView中的dispatch方法有好多好多的功能
"""
return super().dispatch(request, *args, **kwargs) def get(self, request, *args, **kwargs):
return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')
上述是rest framework框架基本流程,重要的功能是在APIView的dispatch中触发。
2.版本
在Django rest-framework中提供了5中version设置方式。
a. 基于url的get传参方式:如:/users?version=v1
REST_FRAMEWORK = {
'DEFAULT_VERSION': 'v1', # 默认版本
'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本
'VERSION_PARAM': 'version' # URL中获取值的key
}
settings.py
from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'^test/', TestView.as_view(),name='test'),
]
urls.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning class TestView(APIView):
versioning_class = QueryParameterVersioning def get(self, request, *args, **kwargs): # 获取版本
print(request.version)
# 获取版本管理的类
print(request.versioning_scheme) # 反向生成URL
reverse_url = request.versioning_scheme.reverse('test', request=request)
print(reverse_url) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容') views.py
views.py
b.基于url的正则方式(推荐使用這种方式):如:/v1/users/
REST_FRAMEWORK = {
'DEFAULT_VERSION': 'v1', # 默认版本
'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本
'VERSION_PARAM': 'version' # URL中获取值的key
}
settings.py
from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'^(?P<version>[v1|v2]+)/test/', TestView.as_view(), name='test'),
]
urls.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import URLPathVersioning class TestView(APIView):
versioning_class = URLPathVersioning def get(self, request, *args, **kwargs):
# 获取版本
print(request.version)
# 获取版本管理的类
print(request.versioning_scheme) # 反向生成URL
reverse_url = request.versioning_scheme.reverse('test', request=request)
print(reverse_url) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容') views.py
Views.py
c.基于 accept 请求头方式:如:Accept: application/json; version=1.0
REST_FRAMEWORK = {
'DEFAULT_VERSION': 'v1', # 默认版本
'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本
'VERSION_PARAM': 'version' # URL中获取值的key
}
settings.py
from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'^test/', TestView.as_view(), name='test'),
]
urls.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import AcceptHeaderVersioning class TestView(APIView):
versioning_class = AcceptHeaderVersioning def get(self, request, *args, **kwargs):
# 获取版本 HTTP_ACCEPT头
print(request.version)
# 获取版本管理的类
print(request.versioning_scheme)
# 反向生成URL
reverse_url = request.versioning_scheme.reverse('test', request=request)
print(reverse_url) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容') views.py
views.py
五、补充知识点
1、若类中有装饰器函数,那么当类加载的时候,装饰器函数就会执行,如下代码:
class Person(object):
@classmethod # 相当于 sleepping = classmethod(sleepping)
def sleepping(cls):
print("Jihong is sleepping") print(sleepping) # 加载类时执行,结果<classmethod object at 0x000001F2C29C8198>
注意:类中直接print语句会执行打印输出结果,而函数只有调用时才会执行,如下:
def func():
print('hello world') # 函数func加载不会执行打印,只有调用即func()才会执行打印
2、__dict__方法
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age def sing(self):
print('I am singing') p1 = Person('alex', 18)
print(p1.__dict__) # {'name': 'alex', 'age': 18}
print(Person.__dict__)
# {'__module__': '__main__', '__init__': <function Person.__init__ at 0x0000021E1A46A8C8>,
'sing': <function Person.sing at 0x0000021E1A46A950>,
'__dict__': <attribute '__dict__' of 'Person' objects>,
'__weakref__': <attribute '__weakref__' of 'Person' objects>,
'__doc__': None}
总结:
对象.__dict__ 返回对象的所有成员字典;
类.__dict__ 返回类的所有成员字典;
我们可以通过(对象.name)取出成员,字典没有这种取值方式,使用对象.name的本质是执行类中的__getitem__方法。
3、现在有如下两个需求:
# 需求一:计算add函数的执行时间(不重写add函数的前提下)
def add(x, y):
return x+y # 解决方式:装饰器
def outer(func):
def inner(*args, **kwargs):
import time
start_time = time.time()
ret = func(*args, **kwargs)
end_time = time.time()
print(end_time - start_time)
return inner @outer
def add(x, y):
return x+y
# 需求二:扩展类中函数的功能(在不重写Father类的前提下)
class Father(object):
def show(self):
print('father show is excuted') father = Father()
father.show() # 解决方式:重新写一个类,继承Father类,重写show(),super()调用
class Son(Father):
def show(self):
print('son show is excuted')
super().show() son = Son()
son.show()
总结:
面向过程的方式对程序进行功能扩展
- 装饰器
面向对象的方式对程序功能进行扩展
- 类的继承
- 方法重写
- super()
4、源码博客:https://www.cnblogs.com/wdliu/category/1211675.html?tdsourcetag=s_pcqq_aiomsg
Restful 1 -- REST、DRF(View源码解读、APIView源码解读)及框架实现的更多相关文章
- drf的基本使用、APIView源码分析和CBV源码拓展
cbv源码拓展 扩展,如果我在Book视图类中重写dispatch方法 -可以实现,在get,post方法执行之前或者之后执行代码,完成类似装饰器的效果 def dispatch(self, requ ...
- CBV源码分析+APIVIew源码分析
{drf,resful,apiview,序列化组件,视图组件,认证组件,权限组件,频率组件,解析器,分页器,响应器,URL控制器,版本控制} 一.CBV源码分析准备工作: 新建一个Django项目 写 ...
- CBV源码与APIView源码解析
一.CBV源码解析 在我们写cbv的时候在url中和fbv的区别就是是否调用了as_view()方法,所以关键入手点就是这个方法 @classonlymethod # 这是类的绑定方法,这个cls是我 ...
- Restful规范-APIView源码分析
目录 一.Restful规范 十条规范 二.drf的简单使用 三.APIView源码分析 CBV源码分析 APIView源码分析 一.Restful规范 Restful规范是一种web API接口的设 ...
- DRF(1) - REST、DRF(View源码解读、APIView源码解读)
一.REST 1.什么是编程? 数据结构和算法的结合. 2.什么是REST? 首先回顾我们曾经做过的图书管理系统,我们是这样设计url的,如下: /books/ /get_all_books/ 访问所 ...
- REST、DRF(View源码解读、APIView源码解读)
一 . REST 前言 1 . 编程 : 数据结构和算法的结合 .小程序如简单的计算器,我们输入初始数据,经过计算,得到最终的数据,这个过程中,初始数据和结果数据都是数据,而计算 ...
- Redux学习之解读applyMiddleware源码深入middleware工作机制
随笔前言 在上一周的学习中,我们熟悉了如何通过redux去管理数据,而在这一节中,我们将一起深入到redux的知识中学习. 首先谈一谈为什么要用到middleware 我们知道在一个简单的数据流场景中 ...
- RxJava系列6(从微观角度解读RxJava源码)
RxJava系列1(简介) RxJava系列2(基本概念及使用介绍) RxJava系列3(转换操作符) RxJava系列4(过滤操作符) RxJava系列5(组合操作符) RxJava系列6(从微观角 ...
- 入口开始,解读Vue源码(一)-- 造物创世
Why? 网上现有的Vue源码解析文章一搜一大批,但是为什么我还要去做这样的事情呢?因为觉得纸上得来终觉浅,绝知此事要躬行. 然后平时的项目也主要是Vue,在使用Vue的过程中,也对其一些约定产生了一 ...
随机推荐
- 谷歌新操作系统fuchsia
开源地址: https://github.com/fuchsia-mirror
- 深度学习在gilt应用——用图像相似性搜索引擎来商品推荐和服务属性分类
机器学习起源于神经网络,而深度学习是机器学习的一个快速发展的子领域.最近的一些算法的进步和GPU并行计算的使用,使得基于深度学习的算法可以在围棋和其他的一些实际应用里取得很好的成绩. 时尚产业是深度学 ...
- ACM学习历程—HDU5418 Victor and World(动态规划 && 状压)
这个题目由于只有16个城市,很容易想到去用状压来保存状态. p[i][state]表示到i城市经过state状态的城市的最优值(state的二进制位每一位为1表示经过了该城市,否则没经过) 这样p[j ...
- ACM学习历程—HDU 4726 Kia's Calculation( 贪心&&计数排序)
DescriptionDoctor Ghee is teaching Kia how to calculate the sum of two integers. But Kia is so carel ...
- poj3177重修道路——边双连通分量缩点
题目:http://poj.org/problem?id=3177 找桥,缩点,总之都是板子: 对于每个叶子,互相连一条边即可:若最后剩下一个,则去和根节点连边: 所以叶子节点数+1再/2即答案. 代 ...
- 推荐几个Laravel 后台管理系统
小编推荐几个Laravel 后台管理系统 由百牛信息技术bainiu.ltd整理发布于博客园 一.不容错过的Laravel后台管理扩展包 —— Voyager 简介Voyager是一个你不容错过的La ...
- mina2中的线程池
一.Mina中的线程池模型 前面介绍了Mina总体的层次结构,那么在Mina里面是怎么使用Java NIO和进行线程调度的呢?这是提高IO处理性能的关键所在.Mina的线程调度原理主要如下图所示: A ...
- source和sh执行脚本时的差异
在CentOS7下,有如下脚:sh02.sh. 1 用sh或者bash执行 先执行echo $firstname $lastname 再执行 sh sh02.sh 最后执行 echo $firstna ...
- SSM之全局异常处理器
1. 异常处理思路 首先来看一下在springmvc中,异常处理的思路: 如上图所示,系统的dao.service.controller出现异常都通过throws Exception向上抛出,最后 ...
- CF-822B
B. Crossword solving time limit per test 1 second memory limit per test 256 megabytes input standard ...