一 . REST

            前言

 1 . 编程 :

  数据结构和算法的结合 .小程序如简单的计算器,我们输入初始数据,经过计算,得到最终的数据,这个过程中,初始数据和结果数据都是数据,而计算过程是我们所说的广义上的算法。

大程序,如一个智能扫地机器人,我们可以设置打扫的距离,左右摆动的幅度来打扫房间,这里面打扫的举例,摆动幅度,都是数据,而打扫的过程是较为复杂的算法过程,总之,也是算法,即程序的实现方式。

另外,我们还可以设置打扫时间等等初始数据。

总之一句话,编程即数据结构和算法的结合。简单的程序可能不需要跟用户交互数据,但是现代的应用程序几乎都需要跟用户进行交互,不分应用程序类型,不管是CS型还是BS型的程序都是如此,而Python最擅长的Web App即BS型的程序,就是通过url和http来跟用户进行数据交互的,通过url和http请求,用户可以操作服务器端的程序,主要操作分为:增、删、改、查几类。

2 . 引入 :

  在以前我们写的图书管理系统中,对于该项目的设计,我们基本上是按照以下方式来实现的.

  传统的url设计风格 

  ● url各式各样,设计混乱

  ● 对于请求处理成功或者失败的返回信息没有明确的响应信息规范,返回给客户端的信息往往和随意.

  理论上来说,这种方式完全可以实现我们的需求,但是一旦项目丰富起来,随着数据量增加,随着各个业务系统之间的逻辑关系不断的复杂,url会越来越复杂,理论上来说,不管是什么类型、什么名称的url都能指向具体的业务逻辑(视图函数),从而实现业务需求,但是如果没有明确的规范,因每个人的思维方式不一样、命名方式不一样而导致的url非常的乱,不方便项目的后期维护和扩展。

3 . rest规范 :

  以上这些情况的出现,导致了很多的问题,让互联网的世界变得杂乱不堪,日益复杂且臃肿。因此http协议创始人警告我们这些凡人们正在错误的使用http协议,除了警告,他还发表了一篇博客,大概意思就是教大家如何正确使用http协议,如何正确定义url,这就是REST(Representational State Transfer),不需要管这几个英文单词代表什么意思,只需要记住下面一句话:

  • 用url唯一定位资源,用Http请求方式(GET, POST, DELETE, PUT)描述用户行为

根据这句话,我们重新定义图书管理系统中的url

  RESTful Api设计风格 : 

  可以看到,url非常简洁优雅,不包含任何操作,不包含任何动词,简简单单,用来描述服务器中的资源而已,服务器根据用户的请求方式对资源进行各种操作。而对数据的操作,最常见的就是CRUD(创建,读取,更新,删除),通过不同的请求方式,就足够描述这些操作方式了。如果不够用,Http还有其他的请求方式呢!比如:PATCH,OPTIONS,HEAD, TRACE, CONNECT。

  REST定义返回结果 :

每一种请求方式的返回结构不相同.

 REST定义的错误信息 : 

{
"error":"Invaild API key"
}

  通过一个字典,返回错误信息

  这就是REST,上图中的url就是根据REST规范设计的 RESTful api. 清晰,简洁.

  REST是只用软甲架构设计风格,不是标准,也不是技术实现,只是提供了一组设计原则和约束条件,是目前最流行的API 设计规范,用于web数据接口的设计。

  RESTful参考链接:

    RESTful API 最佳实践 - 阮一峰

    我所认为的RESTful API最佳实践

  那么,我们接下来要学的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 . 反射

  hasattr , getattr , setattr

class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
self._hobby = 'girls' def show_info(self):
print("show info method been executed") p1 = Person("pizza", 18)
# 查看该对象是否有show_info方法
print(hasattr(p1, "show_info"))
# 查看该对象是否有age属性
print(hasattr(p1, "age"))
print(hasattr(p1, "hahaha")) greeting = "Hello World"
# 设置属性
setattr(p1, "hahaha", greeting)
print(hasattr(p1, "hahaha")) func = getattr(p1, "show_info")
print(func) # <bound method Person.show_info of <__main__.Person object at 0x102219d68>>
# 注意:直接调用,不需要传入self,getattr时已经绑定self到func了
func() print(hasattr(Person, "show_info")) # True
print(hasattr(Person, "name")) # False
print(hasattr(Person, "country")) # False
# 给Person类设置属性
setattr(Person, "country", "china")
print(hasattr(Person, "country")) # True
print(hasattr(p1, "country")) # True

4 . self 定位

  明确self指向谁---始终指向调用者

5 . http请求协议

  协议就是沟通双方约定俗称的规范,即解析数据的规则

  http请求报文包含三部分 : 请求行,请求报文头,请求报文体

注意:post请求类型有formdata,而get没有,下面详细介绍几个常见的请求头信息:

    POST /classbasedview/login/ HTTP/1.1:第一行表示请求行,分别是请求方式(POST),请求URL,HTTP协议版本
HOST:请求行下面的都是请求报文,HOST表示请求的主机和端口号
Connection:请求连接方式
Content-Length:内容长度(字节)
Content-Type:请求数据的编码协议,只有POST,DELETE等请求方式有该部分内容(需重点记忆)
User-Agent:客户端信息
Accept:可接受的响应内容类型
Accept-Encoding:采用什么方式对请求数据进行编码
Cookie:服务器端设置的cookie

注意

6 . form表单的enctype属性中有三种请求协议

  如果通过 form 表单提交用户数据,可以使用form表单的 enctype属性来定义数据编码协议,该属性有三个值,代表三种数据编码协议 :

  - application/x-www-form-urlencoded:使用&符号连接多个键值对,键值对用等号拼接,默认;

  - multipart/form-data:上传文件、图片时使用该方式;

  - text/plain:空格转换为“+”号;

7 . JavaScript中 object,例如: { name: "pizza"}  <==>  json 的相互转换方式

  JSON.stringify(data)  =相当于=>  python json.dumps()

  JSON.parse(data)  =相当于=>  python json.loads()

三 . 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)
#ensure_ascii=False 默认是true,出现中文就是乱码,=False,出现中文就是中文
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:

 >>> 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函数的执行结果,最后将结果返回给用户。

四 . 介绍个工具

postman 工具介绍 :

我们通过postman来模拟用户请求,简单方便,不再需要使用浏览器来发请求了。

安装postman

POSTMAN官网下载之后安装即可。

发送get请求

请看下面的图片:

如图所示,在下拉框中选择GET方式发送请求,在地址栏输入请求url,然后点击send发送即可发送GET请求到后端服务器。

以urlencoded协议发送post请求

请看下图:

选择POST,输入地址,然后在下面的选项框中选择Body,之后选择x-www-form-urlencoded协议,输入key和value,点击Send,即可以urlencoded方式发送post请求,并且在最下面的提示框中可以查看到服务器返回的信息。

以json协议发送post请求

最后,请看下图,以Json协议发送POST请求:

前面都一致,在Body框中,选择raw选项,然后选择application/json,最后输入Json格式的数据,点击Send即可以Json协议发送POST请求。

五 . 知识点补充

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()

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. Restful 1 -- REST、DRF(View源码解读、APIView源码解读)及框架实现

    一.REST 1.什么是编程? 数据结构和算法的结合 2.什么是REST? - url用来唯一定位资源,http请求方式来区分用户行为 首先回顾我们曾经做过的图书管理系统,我们是这样设计url的,如下 ...

  7. DRF之APIView源码解析

    目录 Django项目中的代码如下 APIView源码解析 源码解析总结 Django项目中的代码如下 urls.py中: from django.conf.urls import url from ...

  8. DRF之APIView源码简析

    一. 安装djangorestframework 安装的方式有以下三种,注意,模块就叫djangorestframework. 方式一:pip3 install djangorestframework ...

  9. Django rest framework框架——APIview源码分析

    一.什么是rest REST其实是一种组织Web服务的架构,而并不是我们想象的那样是实现Web服务的一种新的技术,更没有要求一定要使用HTTP.其目标是为了创建具有良好扩展性的分布式系统. 可用一句话 ...

随机推荐

  1. EasyMvc入门教程-基本控件说明(3)时间线

    我们有时候经常看到如下的页面: 或者快递物流信息图标,那么利用EasyMvc如何实现呢?很简单,看下面的例子: @{ var data=new List<TimeLineItem>() { ...

  2. Java程序员新手老手都离不开八大开发工具

    以下这8个工具,从代码构建到错误挤压,覆盖Java开发的全域.学习这些工具可以帮助你改善代码质量,成为一个更高效的Java开发人员.Java这个大世界中正在不断涌现新的工具.实用程序和库.如果你的首选 ...

  3. 微信小程序(应用号)开发新闻客户端的实战课程

    摘要: 本实例将演示从零开发一个微信应用号的过程,页面轮播与跳转传值,实现单元格自定义布局,全部源码可通过github下载. 下载最新版的微信小程序开发工具,目前是v0.9.092300 下载地址:h ...

  4. 日文符号“・”插入sql-server2005乱码问题

    错误:日文符号"・"插入sql-server2005符号.出现乱码 原因:DB字段设为varchar.DB文字编码为"Chinese_PRC_CI_AS" 相应 ...

  5. Shell脚本之:while

    while循环用于不断执行一系列命令,也用于从输入文件中读取数据:命令通常为测试条件.其格式为: while command do Statement(s) to be executed if com ...

  6. Shell脚本之:数组

    bash支持一维数组,并且没有限定数组的大小,数组元素的下标由0开始编号. 定义数组 在Shell中,用括号来表示数组,数组元素用“空格”符号分割开.定义数组的一般形式为: array_name=(v ...

  7. mysql:4种时间类型

    insert 12 ================= 养成良好的习惯,除了整形和浮点型不加'',其余都加,包括日期时间型

  8. Matlab 绘图全方位分析及源码

    Matlab绘图 强大的绘图功能是Matlab的特点之一,Matlab提供了一系列的绘图函数,用户不需要过多的考虑绘图的细节,只需要给出一些基本参数就能得到所需图形,这类函数称为高层绘图函数.此外,M ...

  9. org hibernate querytimeoutexception

    起因 在做Hibernate批量插入时,出现这个错误org.hibernate.QueryTimeoutException: 错误原因是表空间的容量不足,需要加大空间容量:那首先想到的是应该查询其容量 ...

  10. 小练习:用socket实现Linux和Windows之间的通信

    在日常生活中,绝大部分人使用的机器通常是windows系统,可是对于研发人员,开发.编译等工作往往是建立在linux机器上.其实.在服务器方面,Linux.UNIX和WindowsServer占领了市 ...