一、REST

1、什么是编程?

  数据结构和算法的结合。

2、什么是REST?

  首先回顾我们曾经做过的图书管理系统,我们是这样设计url的,如下:

  1.    127.0.0.1:/books/
  2.    127.0.0.1:/get_all_books/ 访问所有的数据
  3.  
  4. 127.0.0.1:/books/{id}/
  5. 127.0.0.1:/books/{id}?method=get 访问单条数据
  6.  
  7. 127.0.0.1:/books/add/
  8. 127.0.0.1:/books/?type=create 创建数据
  9.  
  10. 127.0.0.1:/books/delete/
  11.  
  12. 127.0.0.1:/books/update/

  分析:以上定义的url虽然也可以实现功能,但是因个人命名习惯等的不同,同一个功能会产生五花八门的url,而且响应回去的数据(包括错误提示等)格式也没有统一的规范,这就造成了前后端交互上的困难。

  因此,就产生了REST,REST下的url唯一代表资源,http请求方式来区分用户行为,下面就是符合REST下的url设计规范的示例:

  1.   url的设计规范:
  2.     GET 127.0.0.1:/books/ # 获取所有数据
  3.     GET: 127.0.0.1:/books/{id} # 获取单条数据
  4.     POST 127.0.0.1:/books/ # 增加数据
  5.     DELETE: 127.0.0.1:/books/{id} # 删除数据
  6.     PUT: 127.0.0.1:/books/{id} # 修改数据
  7.   数据响应规范:
  8.     GET 127.0.0.1:/books/ # 返回[{}, {}, {}]
  9.     GET: 127.0.0.1:/books/{id} # 返回单条数据{}
  10.     POST 127.0.0.1:/books/ # 返回添加成功的数据{}
  11.     DELETE: 127.0.0.1:/books/{id} # 返回空""
  12.     PUT: 127.0.0.1:/books/{id} # 返回{} ,更新后完整的一条记录,注意并非一个字段
  13.   错误处理:
  14. { "error": "error_message" }

  REST是一种软件架构设计风格,不是标准,也不是技术实现,只是提供了一组设计原则和约束条件,是目前最流行的API 设计规范,用于web数据接口的设计。2000年,由Roy Fielding在他的博士论文中提出,Roy Fielding是HTTP规范的主要编写者之一。

  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)

  1.   from django.views import View
  2.   class LoginView(View):
  3.   def get(self, request):
  4.   pass
  5.     def post(self, request):
  6.       pass

2、 类方法classmethod & classonlymethod

  1. class Person(object):
  2. def __init__(self, name, age):
  3. self.name = name
  4. self.age = age
  5.  
  6.    # 注意:Person类加载时,会执行装饰器函数classmethod(sleepping),并将结果赋给slepping
  7. @classmethod # 相当于 sleepping = classmethod(sleepping)
  8. def sleepping(cls):
  9. print("Jihong is sleepping")
  10.  
  11. @classonlymethod
  12. def guangjie(cls):
  13. print("Jihong is shopping")
  14.  
  15. Person.sleepping() # 类直接调用类方法
  16. Person.guangjie() # 类直接调用方法
  17. jihong = Person("jihong", 20)
  18. jihong.sleepping() # 对象可以调用类方法
  19. jihong.guangjie() # 报错,对象不能调用由@classonlymethod装饰的方法

  总结:

    @classmethod装饰(python加的装饰器)的方法可以由对象和类调用;

    @classonlymethod装饰(django加的装饰器)只能由类直接调用;

3、反射

  getattr, hasattr, setattr

4、self定位

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

5、http请求协议

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

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应用:

  1. class CourseView(View):
  2. def get(self, request):
  3. course_list = list()
  4.  
  5. for course in Course.objects.all():
  6. course = {
  7. "course_name": course.course_name,
  8. "description": course.description
  9. }
  10.  
  11. course_list.append(course)
  12.  
  13. 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. >>> pip install django # 安装django,已经安装的可以直接下载DRF
  2. >>> pip install djangorestframework # 下载

  安装完成之后,我们就可以开始使用DRF框架来实现我们的web应用了,这部分内容包括如下知识点:

  1.   - APIView
  2.   - 解析器组件
  3.   - 序列化组件
  4.   - 视图组件
  5.   - 认证组件
  6.   - 权限组件
  7.   - 频率控制组件
  8.   - 分页组件
  9.   - 相应器组件
  10.   - url控制器

  介绍DRF,必须介绍APIView,它是重中之重,是下面所有组件的基础,因为所有的请求都是通过它来分发的,至于它究竟是如何分发请求的?想要弄明白这个问题,就必须解读它的源码,而想要解读DRF APIView的源码,就必须先解读django中views.View类的源码,为什么使用视图类调用as_view()之后,请求就可以被不同的函数处理?

1、回顾CBV,解读View源码

  1. # views.py中代码如下:
  2. from django.views import View
  3. class LoginView(View):
  4.   def get(self, request):
  5.     pass
  6.   def post(self, request):
  7.     pass
  8.  
  9. # urls.py中代码如下:
  10. from django.urls import path, include, re_path
  11. from classbasedView import views
  12. urlpatterns = [
  13.   re_path("login/$", views.LoginView.as_view())
  14. ]

  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的相关源码如下:

  1. class View:
  2.   http_method_names = ['get', 'post', 'put', ...]
  3. def __init__(self, **kwargs):
  4. for key, value in kwargs.items():
  5. setattr(self, key, value)
  6.  
  7. @classonlymethod
  8. def as_view(cls, **initkwargs):
  9. for key in initkwargs:
  10. ...
  11. def view(request, *args, **kwargs):
  12.        # 实例化一个对象,对象名称为self,self是cls的对象,谁调用了as_view
  13.        # cls就是谁(当前调用as_view的是LoginView)
  14.        # 所以,此时的self就是LoginView的实例化对象
  15. self = cls(**initkwargs)
  16. if hasattr(self, 'get') and not hasattr(self, 'head'):
  17. self.head = self.get
  18. self.request = request
  19. self.args = args
  20. self.kwargs = kwargs
  21. return self.dispatch(request, *args, **kwargs)
  22. view.view_class = cls
  23. view.view_initkwargs = initkwargs
  24.  
  25. update_wrapper(view, cls, updated=())
  26. update_wrapper(view, cls.dispatch, assigned=())
  27. return view
  28.   def dispatch(self, request, *args, **kwargs):
  29. if request.method.lower() in self.http_method_names:
  30.    # 通过getattr找到的属性,已和对象绑定,访问时不需要再指明对象了
  31.   # 即不需要再:self.handler,直接handler()执行
  32. handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
  33. else:
  34. handler = self.http_method_not_allowed
  35. 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

  使用:

  1. # views.py中代码:
  2. from rest_framework.views import APIView # 引入APIView
  3. class LoginView(APIView): # LoginView继承APIView
  4.   def get(self, request):
  5.     pass
  6.   def post(self, request):
  7.     pass
  8.  
  9. # urls.py中用法同CBV一样,如下示例:
  10. from django.urls import path, include, re_path
  11. from classbasedView import views
  12. urlpatterns = [
  13.   re_path("login/$", views.LoginView.as_view())
  14. ]

  源码解读:

    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的相关源码如下:

  1. class APIView(View):
  2. ...
  3.    # api_settings是APISettings类的实例化对象
  4. parser_classes = api_settings.DEFAULT_PARSER_CLASSES
  5. ...
  6. settings = api_settings
  7. schema = DefaultSchema()
  8.  
  9. @classmethod
  10. def as_view(cls, **initkwargs): # cls指LoginView
  11. if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
  12. ...
  13.   # 下面一句表示去执行APIView父类(即View类)中的as_view方法
  14. view = super(APIView, cls).as_view(**initkwargs)
  15. view.cls = cls
  16. view.initkwargs = initkwargs
  17. return csrf_exempt(view)
  18.  
  19.    def dispatch(self, request, *args, **kwargs):
  20.   ...
  21.   request = self.initialize_request(request, *args, **kwargs)
  22.   ...
  23.   try:
  24.   self.initial(request, *args, **kwargs)
  25.   if request.method.lower() in self.http_method_names:
  26.   handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
  27.   else:
  28.   handler = self.http_method_not_allowed
  29.  
  30.   response = handler(request, *args, **kwargs)
  31.  
  32.   except Exception as exc:
  33.   response = self.handle_exception(exc)
  34.  
  35.   self.response = self.finalize_response(request, response, *args, **kwargs)
  36.   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函数的执行结果,最后将结果返回给用户。

四、补充知识点

1、若类中有装饰器函数,那么当类加载的时候,装饰器函数就会执行,如下代码:

  1. class Person(object):
  2. @classmethod # 相当于 sleepping = classmethod(sleepping)
  3.   def sleepping(cls):
  4.     print("Jihong is sleepping")
  5.  
  6.   print(sleepping) # 加载类时执行,结果<classmethod object at 0x000001F2C29C8198>

  注意:类中直接print语句会执行打印输出结果,而函数只有调用时才会执行,如下:

  1. def func():
  2.   print('hello world') # 函数func加载不会执行打印,只有调用即func()才会执行打印

2、__dict__方法

  1. class Person(object):
  2. def __init__(self, name, age):
  3. self.name = name
  4. self.age = age
  5.  
  6. def sing(self):
  7. print('I am singing')
  8.  
  9. p1 = Person('alex', 18)
  10. print(p1.__dict__) # {'name': 'alex', 'age': 18}
  11. print(Person.__dict__)
  12. # {'__module__': '__main__', '__init__': <function Person.__init__ at 0x0000021E1A46A8C8>,
  13. 'sing': <function Person.sing at 0x0000021E1A46A950>,
  14. '__dict__': <attribute '__dict__' of 'Person' objects>,
  15. '__weakref__': <attribute '__weakref__' of 'Person' objects>,
  16. '__doc__': None}

  总结:

    对象.__dict__ 返回对象的所有成员字典;

    类.__dict__ 返回类的所有成员字典;

    我们可以通过(对象.name)取出成员,字典没有这种取值方式,使用对象.name的本质是执行类中的__getitem__方法。

3、现在有如下两个需求:

  1. # 需求一:计算add函数的执行时间(不重写add函数的前提下)
  2. def add(x, y):
  3.   return x+y
  4. # 解决方式:装饰器
  5. def outer(func):
  6.   def inner(*args, **kwargs):
  7.     import time
  8.     start_time = time.time()
  9.     ret = func(*args, **kwargs)
  10.     end_time = time.time()
  11.     print(end_time - start_time)
  12.   return inner
  13.  
  14. @outer
  15. def add(x, y):
  16.   return x+y
  1. # 需求二:扩展类中函数的功能(在不重写Father类的前提下)
  2. class Father(object):
  3.   def show(self):
  4.     print('father show is excuted')
  5. father = Father()
  6. father.show()
  7. # 解决方式:重新写一个类,继承Father类,重写show(),super()调用
  8. class Son(Father):
  9.   def show(self):
  10.     print('son show is excuted')
  11.     super().show()
  12. son = Son()
  13. son.show()

  总结:

    面向过程的方式对程序进行功能扩展

      - 装饰器

    面向对象的方式对程序功能进行扩展

      - 类的继承

      - 方法重写

      - super()

4、老师博客:https://pizzali.github.io/

DRF(1) - REST、DRF(View源码解读、APIView源码解读)的更多相关文章

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

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

  2. CBV源码与APIView源码解析

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

  3. Restful规范-APIView源码分析

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

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

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

  5. Restful 1 -- REST、DRF(View源码解读、APIView源码解读)及框架实现

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

  6. DRF介绍,DRF项目开发,DRF项目的视图类的dispatch源码解析

    目录 一.DRF介绍 1. 什么是DRF 2. 为什么要用DRF (1)使用DRF的原因 (2)站在开发者的角度来说用DRF的好处(暂时列举这么多) 二.用DRF开发后端项目 三.APIView请求生 ...

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

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

  8. DRF之APIView源码解析

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

  9. DRF之APIView源码简析

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

随机推荐

  1. 转-linux下配置socks5代理

    简介: 在Linux下有各种各样的代理程序可用,象最常用的Squid,是http/https代理,也能代理ftp请求,但它实际上 是个HTTP代理程序,不是ftp代理,但它能处理ftp代理请求,就象浏 ...

  2. Mysql root密码忘记的解决办法

    Windows 版本: 1.打开安装目录下的my.ini 找到 [mysqld] 在下面加入 skip-grant-tables 2. 重启mysql服务 3.打开命令行 依次输入 USE mysql ...

  3. Linux环境下连接Mssql 2008

    首先,Linux环境装个驱动:Microsoft® SQL Server® ODBC Driver 1.0 for Linuxhttps://www.microsoft.com/en-us/downl ...

  4. ctrl +z

    #bg 1 [1]+ /root/bin/rsync.sh & 用 jobs 命令查看正在运行的任务: #jobs [1]+ Running /root/bin/rsync.sh & ...

  5. Windows的静态库使用步骤

    windows库程序: 1.静态库程序 - 运行时不独立存在,会被链接到可执行文件或者动态库中,目标程序的归档. 文件扩展名:LIB 2.动态库程序 - 运行时独立存在,不会被链接到可执行文件或其他动 ...

  6. 在系统重装后为什么ChemDraw用不了

    作为一款非常受欢迎的化学绘图软件ChemDraw需要在满足运行条件的电脑上运行,但是一些用户发现自己在给自己的电脑重装系统之后,ChemDraw运行不了呢.导致ChemDraw用不了的原因比较多样,不 ...

  7. String, JSONArray , JSONObject ,Map<String, Object> 与对象

    String pic = "[{\"picServiceUrl\": \"0f4bb44afb2e48d48b786d3bbdeec283/20180408/6 ...

  8. DB水平切换要点

    分区健选择 数据应该怎样拆分,依照什么纬度来拆分 节点路由 应用程序写死/客户端(TDDL,cobar-client)/中间层(cobar-server) 固定分配/动态分配/混合 分片数据均衡 某些 ...

  9. Android 热修复 Tinker接入及源代码浅析

    本文已在我的公众号hongyangAndroid首发.转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/54882693本文出自张鸿 ...

  10. Java接口成员变量和方法默认修饰符

     Java的interface中,成员变量的默认修饰符为:public static final 所以我们在interface中定义成员变量的时候,可以 1:public static final S ...