一、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源码解读)及框架实现的更多相关文章

  1. drf的基本使用、APIView源码分析和CBV源码拓展

    cbv源码拓展 扩展,如果我在Book视图类中重写dispatch方法 -可以实现,在get,post方法执行之前或者之后执行代码,完成类似装饰器的效果 def dispatch(self, requ ...

  2. CBV源码分析+APIVIew源码分析

    {drf,resful,apiview,序列化组件,视图组件,认证组件,权限组件,频率组件,解析器,分页器,响应器,URL控制器,版本控制} 一.CBV源码分析准备工作: 新建一个Django项目 写 ...

  3. CBV源码与APIView源码解析

    一.CBV源码解析 在我们写cbv的时候在url中和fbv的区别就是是否调用了as_view()方法,所以关键入手点就是这个方法 @classonlymethod # 这是类的绑定方法,这个cls是我 ...

  4. Restful规范-APIView源码分析

    目录 一.Restful规范 十条规范 二.drf的简单使用 三.APIView源码分析 CBV源码分析 APIView源码分析 一.Restful规范 Restful规范是一种web API接口的设 ...

  5. DRF(1) - REST、DRF(View源码解读、APIView源码解读)

    一.REST 1.什么是编程? 数据结构和算法的结合. 2.什么是REST? 首先回顾我们曾经做过的图书管理系统,我们是这样设计url的,如下: /books/ /get_all_books/ 访问所 ...

  6. REST、DRF(View源码解读、APIView源码解读)

    一 . REST            前言 1 . 编程 : 数据结构和算法的结合 .小程序如简单的计算器,我们输入初始数据,经过计算,得到最终的数据,这个过程中,初始数据和结果数据都是数据,而计算 ...

  7. Redux学习之解读applyMiddleware源码深入middleware工作机制

    随笔前言 在上一周的学习中,我们熟悉了如何通过redux去管理数据,而在这一节中,我们将一起深入到redux的知识中学习. 首先谈一谈为什么要用到middleware 我们知道在一个简单的数据流场景中 ...

  8. RxJava系列6(从微观角度解读RxJava源码)

    RxJava系列1(简介) RxJava系列2(基本概念及使用介绍) RxJava系列3(转换操作符) RxJava系列4(过滤操作符) RxJava系列5(组合操作符) RxJava系列6(从微观角 ...

  9. 入口开始,解读Vue源码(一)-- 造物创世

    Why? 网上现有的Vue源码解析文章一搜一大批,但是为什么我还要去做这样的事情呢?因为觉得纸上得来终觉浅,绝知此事要躬行. 然后平时的项目也主要是Vue,在使用Vue的过程中,也对其一些约定产生了一 ...

随机推荐

  1. Mac系统存储-其他存储无故增大

    解决办法:打开Finder:安全倾倒废纸篓就会减少很大一部分存储.

  2. labview初步理解

    1.labview是NI公司开发的一款以图形方式开发程序(G语言)的程序开发环境软件.它的作用与VS一样,是一种程序开发环境.只是vs是以文本语言开发环境而已. 2.labview最广泛应用于开发测试 ...

  3. ASCII UNICODE UTF "口水文"

    最近接了一个单是需要把非 UTF-8 (No BOM)编码的文件转换成 UTF-8 (No BOM),若此文件是 UTF-8 但带有 BOM ,需要转换成不带 BOM 的.于是开启了一天的阅读.首先花 ...

  4. 每天一个linux命令(1):man命令

    版权声明 更新:2017-04-19博主:LuckyAlan联系:liuwenvip163@163.com声明:吃水不忘挖井人,转载请注明出处! 1 文章介绍 本文介绍了Linux下命令man. 2 ...

  5. P2042 [NOI2005]维护数列[splay或非旋treap·毒瘤题]

    P2042 [NOI2005]维护数列 数列区间和,最大子列和(必须不为空),支持翻转.修改值.插入删除. 练码力的题,很毒瘤.个人因为太菜了,对splay极其生疏,犯了大量错误,在此记录,望以后一定 ...

  6. BZOJ4088: [Sdoi2015]立体图

    高一联赛之后不久写的.当时看到这题就感觉特别优美.那个时候啥都不会,就只会这种模拟题,还只会最暴力的方法.对于每个方向的灯,枚举每个位置,手动枚举所有遮挡效果,并在枚举位置过程中传递遮挡效果. con ...

  7. CodeForces - 204C Little Elephant and Furik and Rubik

    CodeForces - 204C Little Elephant and Furik and Rubik 个人感觉是很好的一道题 这道题乍一看我们无从下手,那我们就先想想怎么打暴力 暴力还不简单?枚 ...

  8. configured to save RDB snapshots, but is currently not able to persist o...

    Redis问题 MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on d ...

  9. poj3208启示录——数位DP

    题目:http://poj.org/problem?id=3208 数位DP,首先按位数预处理出每一种位数的情况,包括有多少个魔鬼数和有多少个以6开头的非魔鬼数,以便递推.累加等等: 然后先找出第X个 ...

  10. css3 实现loading效果

    1 圆点渐入渐出 要点: 缩放和透明度的变化,循环变化 <div class="demo1"></div> .demo1{ width: 40px; hei ...