码迷,mamicode.com
首页 > 其他好文 > 详细

drf概述

时间:2019-12-08 00:50:55      阅读:180      评论:0      收藏:0      [点我收藏+]

标签:流行   function   官方   参数   规则   ted   成员   间接   函数返回   

目录
一. 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加的装饰器)只能被==类==调用.

  1. 反射、 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: 空格转换为+号.

  1. JavaScript中的object(如: {name:‘alex‘} <==> json)的相互转换方式
    JSON.stringify(data) ==> 相当于python的 json.dumps()
    JSON.parse(data) ==> 相当于python的 json.loads()
    三. Django REST Framework ( DRF )
  2. 为什么要使用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控制

  1. 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()执行父类的方法

  1. 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概述

标签:流行   function   官方   参数   规则   ted   成员   间接   函数返回   

原文地址:https://www.cnblogs.com/whnbky/p/12004054.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!