zoukankan      html  css  js  c++  java
  • drf概述

    目录
    一. REST

    1. 什么是编程?

    2. 什么是REST?
      二. 知识准备

    3. CBV(class based view)

    4. 类方法 classmethod和classonlymethod

    5. 反射、 isinstance()
      (1)getattr()
      (2)hasattr()
      (3)setattr()
      (4)isinstance()

    6. self定位

    7. http请求协议

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

    9. JavaScript中的object(如: {name:'alex'} <==> json)的相互转换方式
      三. Django REST Framework ( DRF )

    10. 为什么要使用DRF?

    11. 什么时候使用DRF?

    12. DRF的使用
      DRF安装

    13. APIView
      (1)回顾CBV, 解读View源码
      (2)APIView
      四. 补充知识点

    14. 关于装饰器

    15. __dict__方法

    16. 对程序进行功能扩展的两种方式

    17. super()函数
      一. REST

    18. 什么是编程?
      数据结构和算法的结合

    19. 什么是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数据接口的设计.

    参考链接:

    http://www.ruanyifeng.com/blog/2018/10/restful-api-best-practices.html

    http://www.scienjus.com/my-restful-api-best-practices/

    那么, 我们接下来要学习的Django REST Framework与REST有什么关系呢?

    事实上, DRF(Django REST Framework)是一套基于Django开发的, 帮助我们更好的设计符合REST规范的web应用的一个Django App, 所以, 从本质上来讲, DRF是一个Django的App.

    二. 知识准备
    学习DRF之前, 首先回顾一下一个知识点:

    1. CBV(class based view)
      from 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(sleeping),并将结果赋给sleeping

      @classmethod
      def sleeping(cls): # 相当于 sleeping = classmethod(sleeping)
      print("Tom is sleeping")

      @classonlymethod
      def shopping(cls):
      print("Tom is shopping")

    Person.sleeping() # 类直接调用 类方法(用@classmethod装饰)
    Person.Angel() # 类直接调用 方法(用@classonlymethod装饰)

    Tom = Person('Tom', '19')
    Tom.sleeping() # 对象可以直接调用 类方法
    Tom.shopping() # baocuo,对象不能调用由@classonlymethod装饰的方法
    总结:

    @classmethod(python加的装饰器)装饰的方法可以被对象和类调用.
    @classonlymethod(Django加的装饰器)只能被调用.
    3. 反射、 isinstance()
    (1)getattr()
    描述: getattr()函数通过name这个字符串去object中找名字叫做name的属性, 如果找到了, 就返回这个对象的属性值, 如果找不到, 就返回default

    语法:

    getattr(object, name[, default])
    参数:

    object -- 对象
    name -- 字符串, 对象的属性
    default -- 默认返回值, 如果不提供参数, 在没有对应属性时, 将触发AttributeError
    返回值: 返回对象属性

    (2)hasattr()
    描述: hasattr()函数用于判断对象是否包含对应的属性.

    语法:

    hasattr(object, name)
    参数:

    object -- 对象
    name -- 字符串, 属性名
    返回值: 如果对象object中存在一个名字叫做name的属性, 那么就返回True, 否则返回False.

    (3)setattr()
    描述: setsttr()函数对应函数getsttr(), 用于给对象object的name的属性重新设置值, 注意该属性name必须存在.

    语法:

    setattr(object, name, value)
    参数:

    object -- 对象
    name -- 字符串, 对象属性
    value -- 属性值
    返回值: 无

    (4)isinstance()
    描述: isinstance()函数用来判断一个对象是否是一个已知的类型.

    isinstence()与type()的区别:

    -- type()不会认为子类是一种父类类型,不考虑继承关系.
    -- isinstence()会认为子类是一种父类类型,考虑继承关系.
    如果要判断两个类型是否相同推荐使用isinstence().
    语法:

    isinstence(object, classinfo)
    参数:

    object -- 实例对象
    classinfo -- 可以是直接或间接类名, 基本类型或者由它们组成的元组
    返回值: 如果对象的类型与参数二的类型(classinfo)相同则返回True, 否则返回False.

    1. self定位
      我们要明确, self指向的始终是调用者.

    2. http请求协议
      协议就是沟通双方约定俗成的规范, 也即是解析数据的规则

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

    application/x-www-form-urlencoded: 该值使用&符号链接多个键值对, 键值对用等号拼接. 该值是enctype属性的默认值.
    multipart/form-data: 上传文件, 图片时使用该值.
    text/plain: 空格转换为+号.
    7. JavaScript中的object(如: {name:'alex'} <==> json)的相互转换方式
    JSON.stringify(data) ==> 相当于python的 json.dumps()
    JSON.parse(data) ==> 相当于python的 json.loads()
    三. Django REST Framework ( DRF )

    1. 为什么要使用DRF?
      DRF从本质上来讲, 它就是一个Django的App, 有了这样一个App, 我们就可以更好的设计出符合RESTful规范的web应用. 实际上, 即便没有DRF, 我们也能够自行设计出符合RESTful规范的web应用, 如下示例:

    from django.shortcuts import HttpResponse
    from django.views import View
    from * import models
    import json
    class CourseView(View):
    def get(self, request):
    course_list = list()

        for course in models.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就是这样一个优秀的工具, 另外, 它不仅能够帮助我们快速的设计出符合RESTful规范的接口, 还提供了诸如 认证 , 权限 等等其他强大的功能.

    1. 什么时候使用DRF?
      RESTful 是目前最流行的API设计规范, 如果使用Django开发你的web应用, 那么请尽量使用DRF, 如果使用的是Flask, 则可以使用Flask-RESTful.

    2. DRF的使用
      DRF官方文档

    DRF安装

    安装django

    pip install django

    安装djangorestframework

    pip install djangorestframework
    安装完成以后, 我们就可以开始使用DRF框架来实现我们的web应用了. 该框架包含以下知识点:

      - APIView
      - 解析器组件
      - 序列化组件
      - 视图组件
      - 认证组件
      - 权限组件
      - 频率控制组件
      - 分页组件
      - 相应器组件
      - url控制
    4. APIView
    介绍DRF, 必须介绍APIView, 它是重中之重, 是下面所有组件的基础, 因为所有的请求都是通过APIView来分发的. 那么它究竟是如何来分发请求的呢? 想要弄明白这个问题, 就必须解读它的源码, 而想要解读DRF的APIView源码, 就必须首先弄清楚django中views.View类的源码, 为什么使用了视图函数类调用as_view()之后, 请求就可以被不同的函数处理了呢?

    (1)回顾CBV, 解读View源码

    urls.py中代码如下:

    from django.urls import path, include, re_path
    from classbasedView import views
    urlpatterns = [
    re_path('login/$', views.LoginView.as_view())
    ]

    views.py中代码如下:

    from django.views import View
    class LoginView(View):
    def get(self, request):
    pass
    def post(self, request):
    pass
    以上代码执行流程如下(源码解读):

    (1)启动django项目, 即执行了python manage.py runserver 127.0.0.1:8000之后
    (2)开始加载settings.py配置文件
    读取models.py
    加载views.py
    加载urls.py, 执行as_view(): views.LoginView.as_view()
    (3)由于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 hassttr(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 = initwargs
        
        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_name:
            # 通过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与某一个函数的对应关系建立, 并开始等待用户请求.

    (4)当用户发来请求(如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函数的执行结果, 最后将结果返回给用户.
    (2)APIView
    使用:

    views.py中代码:

    from rest_framework.views import APIView # 引入APIView
    class LoginView(APIView): # 继承APIView
    def get(self, request):
    pass
    def post(self, request):
    pass

    urls.py中代码:

    from django.urls import path, include, re_path
    from classbasedView import views
    urlspatterns = [
    re_path('login/$', views.LoginView.as_view())
    ]
    源码解读:

    (1)启动django项目: python manage.py runserver 127.0.0.1:8000
    (2)开始加载settings.py文件
    (3)读取models.py文件
    (4)加载views.py文件
    (5)加载urls.py文件, 执行as_view(): views.LoginView.as_view()
    (6)由于LoginView中没有as_view, 因此去执行父类APIView中的as_view方法, 父类APIView的相关源码如下:
    class APIView(View):
    ...
    # api_settings是APISettings类的实例化对象
    parser_classes = api_settings.DEFAULT_PARSER_CLASSES
    ...
    settings = api_setings
    schema = DefaultSchema()

    @classmethod
    def as_view(cls, **initkwargs):     # cls指LoginView
        if isinstence(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_name:
                handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            response = self.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对应关系已经建立, 等待用户请求.

    (7)当用户发来请求(如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 # 相当于 sleeping = classmethod(sleeping)
    def sleeping(cls):
    print('Tom is sleeping')

    print(sleeping) 
    # 加载类时执行,结果是:<classmethod object at 0x000001F2C29C8198>
    

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

    def func():
    print('Hello World!')

    函数func 加载 不会执行打印

    而只有当我们调用 func() 才会执行打印

    1. __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 方法.

    1. 对程序进行功能扩展的两种方式
      现在有如下两种需求:

    需求一: 计算下面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. super()函数
    描述: super()函数是用于调用父类(超类)的一个方法. super是用来解决多重继承问题, 直接用类名调用父类方法在使用单继承的时候没有问题, 但是如果使用多继承, 会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题.

    MRO就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表.

    语法:

    super(type[, object-or-type])
    参数:

    type -- 类
    object-or-type -- 类, 一般是self
    返回值: 无

    注意: Python3.x 和 Python2.x的一个区别是: Pyhton3 可以直接使用 super().xxx 代替 super(Class, self).xxx.

    Python3.x与Python2.x中super()的用法对比:

    Python3.x实例:

    class A:
    pass
    class B(A):
    def add(self, x):
    super().add(x)

    Python2.x实例:

    class A(object): # Python2.x记得要继承object
    pass
    class B(A):
    def add(x):
    super(B, self).add(x)
    实例说明:

    class FooParent(object):
    def init(self):
    self.parent = 'I am the parent!'
    print('Parent')

    def bar(self, message):
        print('%s from Parent!' % message)
    

    class FooChild(FooParent):
    def init(self):
    '''
    以super(B, self)的格式作为参照,
    super(FooChild, self)首先找到FooChild的父类(即FooParent),
    然后把类B(即super()的第一个参数)的对象FooChild转换为FooParent的对象
    '''
    super(FooChild, self).init()
    print('Child')

    def bar(self, message):
        super(FooChild, self).bar(message)
        print('Child bar function!')
        print(self.parent)
    

    if name == 'main':
    fooChild = FooChild()
    fooChild.bar('HelloWorld')

    执行结果:

    Parent

    Child

    HelloWorld from Parent!

    Child bar fuction!

    I am the parent!

  • 相关阅读:
    初试 spring web mvc
    读取网络数据缓存在本地 流程图
    servlet 过滤器实现 请求转发(跳转);跨域转发请求;tomcat 环境下。
    C# .net基于Http实现web server(web服务)
    微信公众平台开发
    Linux目录结构及作用
    MySQL事件调度器event的使用
    MySQL触发器trigger的使用
    存储过程的查、改、删
    MySQL游标的简单实践
  • 原文地址:https://www.cnblogs.com/whnbky/p/12004054.html
Copyright © 2011-2022 走看看