DRF概述
一. 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数据接口的设计.
参考链接:
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.
4. self定位
我们要明确, self指向的始终是调用者.
5. http请求协议
协议就是沟通双方约定俗成的规范, 也即是解析数据的规则
6. 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规范的接口, 还提供了诸如 认证 , 权限 等等其他强大的功能.
2. 什么时候使用DRF?
RESTful 是目前最流行的API设计规范, 如果使用Django开发你的web应用, 那么请尽量使用DRF, 如果使用的是Flask, 则可以使用Flask-RESTful.
3. 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() 才会执行打印
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. 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!
DRF概述的更多相关文章
- 写写Django中DRF框架概述以及序列化器对象serializer的构造方法以及使用
		写写Django中DRF框架概述以及序列化器对象serializer的构造方法以及使用 一.了解什么是DRF DRF: Django REST framework Django REST framew ... 
- drf框架安装配置及其功能概述
		0902自我总结 drf框架安装配置及其功能概述 一.安装 pip3 install djangorestframework 二.配置 # 注册drf app NSTALLED_APPS = [ # ... 
- DRF 缓存解决方案 drf-extensions / redis
		drf-extensions 概述 drf-extensions组件内部提供了 DRF 的本地内存方式的缓存方式 本地内存方式缓存在项目重启后则会消失 官方点击 这里 安装 pip3 install ... 
- 一: DRF web应用框架基础,及序列化器的使用
		---恢复内容开始--- 一: web 应用模式(有两种) 1: 前后端不分离(前端从后端直接获取数据) 2: 前后端分离 二: api 接口 原因一: 为了在团队内部形成共识.防止个人习惯差异引起的 ... 
- python 全栈开发,Day104(DRF用户认证,结算中心,django-redis)
		考试第二部分:MySQL数据库 6. MySQL中char和varchar的区别(1分) char是定长,varchar是变长. char的查询速度比varchar要快. 7. MySQL中va ... 
- python 全栈开发,Day99(作业讲解,DRF版本,DRF分页,DRF序列化进阶)
		昨日内容回顾 1. 为什么要做前后端分离? - 前后端交给不同的人来编写,职责划分明确. - API (IOS,安卓,PC,微信小程序...) - vue.js等框架编写前端时,会比之前写jQuery ... 
- DRF的认证、权限 和 限制
		一.概述 认证是将传入请求与一组标识凭据(例如请求来自的用户或其签名的令牌)相关联的机制.然后 权限 和 限制 组件决定是否拒绝这个请求. 简单来说就是: 认证确定了你是谁 权限确定你能不能访问某个接 ... 
- DRF框架学习总结
		DRF框架安装配置及其功能概述 Django与DRF 源码视图解析 DRF框架序列化和返序列化 DRF框架serializers中ModelSerializer类简化序列化和反序列化操作 DRF源码s ... 
- drf框架中认证与权限工作原理及设置
		0909自我总结 drf框架中认证与权限工作原理及设置 一.概述 1.认证 工作原理 返回None => 游客 返回user,auth => 登录用户 抛出异常 => 非法用户 前台 ... 
随机推荐
- spring security 学习资料
			spring security 学习资料 网址 Spring Security 文档参考手册中文版 https://springcloud.cc/spring-security.html 
- cookie,,session,cache,static 浅谈笔记
			cookie:存在于客户端,每次请求伴随httpRequest 到达服务器端,Cookie内保存sessionId,服务器端就是根据Cookie中的SessionId找到用户的专有session , ... 
- tomcat学习之路
			一:介绍目录文件夹 bin文件夹:放置可执行文件 startup.bat:脚本文件,windows环境,起服务 shutdown.bat:脚本文件,windows环境,关闭 startup.sh:脚本 ... 
- 2019 Nowcoder Multi-University Training Contest 1 H-XOR
			由于每个元素贡献是线性的,那么等价于求每个元素出现在多少个异或和为$0$的子集内.因为是任意元素可以去异或,那么自然想到线性基.先对整个集合A求一遍线性基,设为$R$,假设$R$中元素个数为$r$,那 ... 
- 主语,that和which
			但除了名词以外还可以用代词.动名词.不定式短语,名词性从句.名词短语.表距离的地点副词短语等做主语. 1.名词作主语. The child is my daughter. 2.代词做主语. He al ... 
- system.stat[resource,<type>]
			系统信息. 整型或者浮点型 ent - 该分区有权接收的处理器单元数(float) kthr, - 关于内核线程状态的信息: r - 平均可运行内核线程数(float) b - 虚拟内存管理器等待队列 ... 
- oracle 使用length()函数需要注意的坑!
			1.情景展示 筛选出指定字段字符长度既不等于18也不等于15的数据. 2.原因分析 第一步:按字符串度进行分组统计: 第二步:筛选数据. 你会发现,只将length=17统计了出来,长度不存在的数 ... 
- 查看大图、html查看大图、js查看大图
			$(".pimg").click(function(){ var _this = $(this);//将当前的pimg元素作为_this传入函数 imgShow("#ou ... 
- js获取数组中的最大值/最小值
			目录 前言 1. 使用Math的静态方法max/min 1.1 结合ES6的扩展运算符...使用 1.2 结合apply/call方法来使用 1.3 结合reduce来使用 2. 排序获取 2.1 只 ... 
- uniapp - 更改项目生成模板、页面
			每次生成项目目录都需要删除一些再添加太麻烦了,就想着能不能修改一下模板 - 当然自定义模板也能实现 好了,被我找到了. 修改以后源文件名称和格式覆盖回去即可,重新启动hbuilderx 这里可以修改e ... 
