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

一.REST

1.什么是编程?

  数据结构和算法的结合

2.什么是REST?

  首先我们回顾下我们之前的图书管理系统,我们设计了这样的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规范的主要编写者之一。

  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、反射

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

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

四、补充知识点

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://pizzali.github.io/

原文地址:https://www.cnblogs.com/kenD/p/10367479.html

时间: 2024-10-09 20:41:22

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

DRF之APIView源码解析

目录 Django项目中的代码如下 APIView源码解析 源码解析总结 Django项目中的代码如下 urls.py中: from django.conf.urls import url from app import views urlpatterns = [ url(r'^test/$', views.APIViewSourceCode.as_view()), ] views.py中: from rest_framework.views import APIView class APIVi

rest_framework-00-规范-APIview源码解析-认证

rest_framework-00-规范-APIview源码解析-认证 规范 支付宝: 接口开发 订单api----order 方式1:缺点:如果有10张表,则需要40个url. urls.py views.py 缺点:如果有10张表,则需要40个url.    接下来就出现了resrful 规范,比较简洁 方式2:resrful 规范(建议)  url简洁了,只有一条. 1. 根据method不同做不同的操作,示例:基于FBV: urls.py views.py 2. 根据method不同做不

APIView源码简单分析图

APIView源码简单分析 !声明:下面这个dispatch分发方法不在是父类View里的dispatch了,APIView重新封装了这个dispatch.(整个核心就是initialize_request这个方法). !返回后,回到dispatch方法,接下来执行就是三大组件了.认证,权限,频率 原文地址:https://www.cnblogs.com/lujiachengdelu/p/10433533.html

Django中CBV和Restful API中的APIView源码分析

Django中CBV和Restful API中的APIView源码分析 python的Django框架的视图处理可以用FBV, 也可以采用CBV.首先定义一个CBV视图: from django.views import Viewfrom django.http import JsonResponseclass Book(View):    def get(self, request):        ll = [{'key':value}]        return JsonResponse

CVB生命周期(APIView源码解析)

目录 Django项目中的代码如下 APIView源码解析 源码解析总结 Django项目中的代码如下 urls.py中: from django.conf.urls import url from app import views urlpatterns = [ url(r'^test/$', views.APIViewSourceCode.as_view()), ] views.py中: from rest_framework.views import APIView class APIVi

SpringMVC源码解读 - RequestMapping注解实现解读 - RequestCondition体系

一般我们开发时,使用最多的还是@RequestMapping注解方式. @RequestMapping(value = "/", param = "role=guest", consumes = "!application/json") public void myHtmlService() { // ... } 台前的是RequestMapping ,正经干活的却是RequestCondition,根据配置的不同条件匹配request. @Re

Python之美[从菜鸟到高手]--浅拷贝、深拷贝完全解读(copy源码分析)

可悲的我一直以为copy模块是用C写的,有时候需要深入了解deepcopy,文档描述的实在太简单,还是不知所云. 比如说最近看sqlmap源码中AttribDict的_deepcopy__有些疑惑, def __deepcopy__(self, memo): retVal = self.__class__() memo[id(self)] = retVal for attr in dir(self): if not attr.startswith('_'): value = getattr(se

js便签笔记(9)——解读jquery源码时记录的一些知识点

近来一直利用业余时间在看jquery2.1.1源码,大约看了两千行了.平时看的时候,做了一些笔记,贴出来分享. 1. Array.prototype.slice.call 可以将伪数组转化为真正的数组 其实,这里所谓的"伪数组"就是有length属性,并且有"0"."1"."2"等这些属性的对象,如下代码: var obj = { 0: "A", 1: "B", 2: "C&q

SpringMVC源码解读 - RequestMapping注解实现解读 - RequestMappingInfo

使用@RequestMapping注解时,配置的信息最后都设置到了RequestMappingInfo中. RequestMappingInfo封装了PatternsRequestCondition,RequestMethodsRequestCondition,ParamsRequestCondition等,所以自己不干活,所有的活都是委托给具体的condition处理. 先看下封装的RequestCondition吧,之前的文章将的比较细了,不清楚各个类具体是做什么的,可以移步这里<Sprin