drfCBV
CBV 看源码逻辑那部分的那张图,大体的就晓得啦..
drf的request --> request.query_params 等同于 request._request.GET
2
# 前后端分离
什么是前后端分离?
1.从程序的角度
前后端不分离:
浏览器发送请求 --> Django框架(MTV),处理所有资源 --> 返回浏览器呈现
Ps:模版渲染的工作是Django在做
*扩展开,uwsgi、中间件等.
前后端分离: 分为两套代码-前端代码+后端代码
step1> 浏览器向前端发送请求 --> 前端返回html+css+js给浏览器,浏览器呈现空荡荡的页面,没有数据
step2> 浏览器运行js代码向后端发送ajax请求 --> 后端返回json格式的数据 --> 浏览器通过js代码将数据渲染到页面上
Ps:模版渲染的工作跟后端没有关系啦
2.从专业角度
前端:前端代码 eg:网页、APP、微信小程序 -- 常见技术 vue.js react.js angular.js
后端:API -- Django Django-restframeworks
★并不是说前后端分离就比前后端不分离好.
一般来说:
不分离 -- 应用于后台系统、用户量少
分离 -- 应用于用户系统、用户量多、专业人士
以一个教育平台为例,业务可分为三大块:
1> Web平台 -- 前后端分离
2> 管理后台(管理人员登陆后台) -- 前后端不分离
3> 导师后台(学生上传作业,老师登陆后台批改) -- 前后端不分离
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# FBV与CBV
# 代码实现
Django和drf的 FBV 与 CBV 如何实现? (代码)
Django的路由有CBV和FBV的写法, DRF的路由也有CBV和FBV的写法!
drf 安装 - 注册 - 路由 - 视图
post请求的测试得注销掉csrf中间件.
路由
from django.urls import path
from app01 import views
urlpatterns = [
# --- Django
path('auth/<int:pk>/', views.auth), # -- Django FBV
path('user/<str:dt>/', views.UserView.as_view()), # -- Django CBV
# --- drf
path('login/', views.login), # -- DRF FBV
path('info/', views.InfoView.as_view()), # -- DRF CBV
]
2
3
4
5
6
7
8
9
10
11
视图
# --- Django
from django.http import JsonResponse
from django.views import View
def auth(request, *args, **kwargs): # def auth(request, pk):
print(args) # ()
print(kwargs) # {'pk':1}
if request.method == "GET":
return JsonResponse({"status": True, "message": "get"})
elif request.method == "POST":
return JsonResponse({"status": True, "message": "post"})
class UserView(View):
def get(self, request, *args, **kwargs):
return JsonResponse({"status": True, "message": "get"})
def post(self, request, *args, **kwargs):
return JsonResponse({"status": True, "message": "post"})
# --- drf
from rest_framework.decorators import api_view
from rest_framework.views import APIView
from rest_framework.response import Response
@api_view(["GET"])
def login(request):
return Response({"status": True, "message": "success"})
class InfoView(APIView):
def get(self, request):
return Response({"status": True, "message": "success"})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 源码逻辑
自己理一理, 详细的细节可以看以往的笔记.
简单来说, Django和DRF的CBV写法, 返回的都是 view函数.. 但DRF得到的是 免除CSRF认证的view..
当路由匹配成功后, view加括号执行, drf 在里面干了几件事: (在drf的dispatch方法里干的)
1> request的二次封装
2> 认证、权限、限流、版本器、解析器
3> dispatch 反射, 执行视图函数(get post put delete)
4> 返回
下方截图 蓝-绿-黄 的顺序查看!
先警醒一点: cls是类InfoView, self是类InfoView的实例!
再提醒一点: as_view()里的**initkwargs用作视图类实例化时构造函数里的参数;view()里的*args、**kwarg可接收动态路由上传递的参数.
再注意三点: drf和Django的as_view都是类方法 ;
类方法里使用super,super函数的第二个参数是cls.
request是何时传的? 路由匹配成功时.和动态路由参数一起传过去的,request是位置参数,所以需先传.
详看前面的源码分析.看上述简易版的,也可以自己大概捋清是怎么一回事!! 无非就是闭包、反射、继承关系.
0. Django和drf的cbv都是 --> 闭包 + 反射
1. 思考下,路由如何传递的参数?
1> path('auth/<int:pk>/', views.auth),若网址是127.0.0.1:8000/auth/1/ 是以关键字参数的形式将 pk=1 传递给auth函数
auth函数可以通过 def auth(request,pk):.. 或者 def auth(request,*args,**kwargs):.. 进行接收.
Ps:pk会在kwargs里.
2> ★★★ 在Django和drf的CBV里,都会执行view “加括号执行”,也就是说,匹配成功后
会将路由中的参数给到 def view(request,*args,**kwargs)里;
然后再将参数传递给 self.dispatch(request,*args,**kwargs) 函数;
然后再通过反射传递给了相应的get、post..方法. handler(request,*args,**kwargs)
<底层是怎么加括号的,路由上的参数怎么就传递到view函数的*args和**kwargs里的,不必深究.>
2. 思考下,Django和drf的request有何不同?
Django的request -- 包含请求相关的所有数据
drf的request -- 在APIView的dispatch方法中进行了二次封装!(包含原来的request,也新添加了一些东西)
Ps:在视图中想获取原来request的值,可以通过 request._request.GET request._request.POST
3.注意一点,通过源码,可以得知 在drf的视图函数中,可以通过 self.args、self.kwargs 获取路由传递的参数!!
# 关键源码如下:
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
补充说明:
1>
self = cls(**initkwargs) # --!! 准确点说,self是InfoView的实例!!传入的cls的类InfoView!
类APIView的实例,**initkwargs意味着在路由里写代码时,path('info/', views.InfoView.as_view()),
这个as_view(),括号里是可以传递参数进去的,用作类实例化.
更有意思的是,APIView里面没有__init__,APIView继承View,所以类实例化时,它用的View的__init__
view的__init__是这样写的,很值得学习.
class View:
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
2> drf的dispath里
request = self.initialize_request(request, *args, **kwargs) # 二次封装request
self.initial(request, *args, **kwargs) # 认证、权限、限流
3> 其实Django的as_view里的代码 self.setup(request, *args, **kwargs)
setup方法里,就执行了 self.args = args self.kwargs = kwargs 不重要,不知道也无妨.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 路由参数传递
下面简单粘贴参数相关的代码!! 简单来说, path转换器 -- kwargs; 无名分组 -- args; 有名分组 -- kwargs;
1 path转换器
2 无名分组
3 有名分组
# drf项目(纯净版)
注意, 若是纯净版的, 需要在settings文件中加上drf匿名用户的配置..
创建指定版本的Django项目(详看前面笔记)
安装djangorestframework,并注册!! (之所以注册是因为注册后可以用到该app里的template和静态文件等)
INSTALLED_APPS = [
# 'django.contrib.admin',
# 'django.contrib.auth',
# 'django.contrib.contenttypes',
# 'django.contrib.sessions',
# 'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.ApiConfig',
'rest_framework',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
# 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
# 'django.contrib.auth.middleware.AuthenticationMiddleware',
# 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
# 'django.contrib.auth.context_processors.auth',
# 'django.contrib.messages.context_processors.messages',
],
},
},
]
# --- drf配置!!!
# 不配置的话,访问接口时会报错: (认证组件匿名用户导致的报错)
# Model class django.contrib.contenttypes.models.ContentType doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.
REST_FRAMEWORK = {
"UNAUTHENTICATED_USER": None, # 设置 request.user,其值为一个lambda表达式也可以
"UNAUTHENTICATED_TOKEN": None, # 设置 request.auth
}
# -- urls根路由
# from django.contrib import admin
from django.urls import path
urlpatterns = [
# path('admin/', admin.site.urls),
]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# OOP知识
Q: 类中的
__getattribute__
方法,"称为属性拦截器" 什么时候触发?
- 首先要明白, 只要执行 "对象.成员" 都会执行
__getattribute__
- 若自定义类继承的Object类, Object类中的
__getattribute__
其内部的处理机制如下:
- 按照属性查找规则,对象或类中有该成员,返回该成员的值
- 都无该成员, 会抛出 AttributeError 的异常!! (该异常后续会触发
__getattr__
的执行)Q: 类中的
__getattr__
方法什么时候触发?
- 当我们以 "对象.成员" 的方式去访问对象和类中不存在成员时(
__getattribute__
会抛出异常) , 触发该方法的执行!!
简单来说, 使用 对象/实例 . 成员
或 实例应用反射getattr 是前提, 会自动调用父类的 __getattribute__
, 如果属性存在, 它会直接返回;
如果实例和实例所在类都没有该属性, 那么会检测我们是否定义了 __getattr__
定义了则执行, 没定义则抛出 __getattribute__
中的异常 AttributeError..
(上述可没说, 类.成员 啥的!! 因为 类.成员 有些许不同.. 暂不深究! 在前面的博文,手撸ORM那也有提及)
注意一点!! 若 直接对象.__getattribute__()
执行该方法, 只是调用一个方法罢了, 不会涉及到 __getattr__
...
再注意一点!! 重写的__getattribute__
方法里不能 访问对象不存在的成员, 不然会陷入死递归!! 重写的__getattr__
也一样.
有些情况会跳过 __getattribute__ 详见:
https://docspy3zh.readthedocs.io/en/latest/reference/datamodel.html#object.__getattribute__
2
# 对象.成员
class Request(object):
v1 = "Hello"
def __init__(self, req, xx):
self._request = req
self.xx = xx
obj = Request(1, 2)
print(obj.xx)
print(getattr(obj, "xx"))
print(obj._request)
print(obj.v1)
"""
2
2
1
Hello
"""
▲ 提醒一下,类中的绑定方法也是对象的成员..
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# __getattribute__
实际上! 获取对象中的成员时, 本质是调用
__getattribute__
方法, 默认我们不定义就用父类中的
所以下面该程序的执行逻辑跟上面那个程序并无出入.. 剖析的更深入一点罢了.
class Request(object):
def __init__(self, req, xx):
self._request = req
self.xx = xx
def __getattribute__(self, item):
print("执行__getattribute__:", item)
return super().__getattribute__(item)
obj = Request(1, 2)
print(obj.xx)
print(obj._request)
# 注意: 该程序执行 obj.v1 会报错, 因为obj和Request中都没有v1这个成员
"""
执行__getattribute__: xx
2
执行__getattribute__: _request
1
"""
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# __getattr__
加入
__getattr__
, 对象.成员 时候 思考的要更多一点!
step1: 只要执行 "对象.成员" 都会执行__getattribute__
, 一般来说, 我们默认不定义, 会直接用父类object中的;
step2: object中的__getattribute__
的源码看不到, 但逻辑大致是这样的:
1> 对象或者类中有该成员, 直接获取该成员的值并返回;
2> 若对象和类中都没有该成员, 抛出异常.
step3: 若步骤2中抛出了异常, 会捕获, 调用类中的__getattr__
方法, 但如果类中没有自定义__getattr__
方法, 直接报错!!
注意: 步骤3中阐述的类不是指object类, 而是我们自己写的类, 比如下面代码的 Request类..也就是说,
__getattr__
是 实例引用属性实例.属性
时,按照正常的规则进行属性查找, 没找到时的兜底函数.
Ps: 前面的博文中也有对该方法的讲解!! 详情自行查阅.
class Request(object):
def __init__(self, req, xx):
self._request = req
self.xx = xx
def __getattribute__(self, item):
print("执行__getattribute__:", item)
return super().__getattribute__(item)
def __getattr__(self, item):
print("执行__getattr__:", item)
return 999
obj = Request(1, 2)
print(obj.xx)
print(obj._request)
print(obj.v1)
"""
执行__getattribute__: xx
2
执行__getattribute__: _request
1
执行__getattribute__: v1
执行__getattr__: v1
999
"""
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
扩展说明下:
class B:
def __init__(self):
self.name = "夏色祭"
self.age = -1
def __getattr__(self, item):
try:
return self.__dict__[item]
except KeyError:
raise AttributeError
print(getattr(B(), "NAME", "不存在的属性")) # 不存在的属性
"""
该程序会调用我们重写的__getattr__
然后该方法通过字典返回, 但是注意: 在__getattr__里面可千万不能通过.来访问一个不存在的属性, 因为那样会陷入无限递归
(想一想能解释的通,点语法访问一个不存在的属性,肯定会再调用__getattr__!!)
(这里self中是存在__dict__这个属性的)
针对,反射getattr()而言:
如果存在的话, 直接返回; 但如果不存在, 一定要raise AttributeError, 这样的话才会返回getattr的第三个参数, 即默认值
如果是其它错误, getattr是无法捕获的; 正如自定义迭代器要raise StopIteration一样, 只有这样for循环才会捕捉到并终止迭代
"""
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 伪装术
oop里的 组合 !!
class HttpRequest(object):
def __init__(self):
pass
def v1(self):
print("v1")
def v2(self):
print("v2")
class Request(object):
def __init__(self, req, xx):
self._request = req
self.xx = xx
request = HttpRequest()
request.v1()
request.v2()
request = Request(request, 111)
request._request.v1()
request._request.v2()
"""
v1
v2
v1
v2
"""
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
对象封装/组合 结合 __getattr__
和 __getattribute__
两个方法的使用!!
class HttpRequest(object):
def __init__(self):
pass
def v1(self):
print("v1")
def v2(self):
print("v2")
class Request(object):
def __init__(self, req, xx):
self._request = req
self.xx = xx
"""
前提逻辑,Request类和Request类的实例中没有attr,去HttpRequest和HttpRequest的实例中找attr.
下面的解释慢慢看能看懂,但我当时解释的太复杂太啰嗦了. 简单来说:
- 针对1>
self._request是HttpRequest类的实例,HttpRequest类和HttpRequest类实例都没有attr
+ 加上
HttpRequest类中无兜底的函数 __getattr__ 报错.
- 针对2>
"1>"中报错,对吧? ok,我将其捕获.
单独执行 self.__getattribute__(attr) 来个偷梁换柱
该语句肯定会报错的,因为此处的self是指Request的实例,能执行到这里, 肯定没有该attr..
"☆ 别整混了,当点语法时,自动执行的__getattribute__报错,会接着执行兜底函数..
这里是单独调用__getattribute__!!报错就报错了,没有后续的自动处理啥的."
1> 若Request的__getattr__方法没有写try..except.. 函数体直接返回 return getattr(self._request, attr)
那么, request.v3 报错 --> AttributeError: 'HttpRequest' object has no attribute 'v3'
怎么个执行逻辑呢?
return getattr(self._request, attr) 等同于 return self._request.attr
因为self._request存在,所以不会执行Request类里的__getattr__方法.
但若self._request和self._request所在类HttpRequest中没有attr成员
(即HttpRequest类和HttpRequest类的实例中都没有)
并且HttpRequest类中没有__getattr__方法来兜底,直接报错!!
(▽ 此异常是 HttpRequest类中的__getattribute__中抛出的!!)
2> 若写了try..except..
那么, request.v3 报错 --> AttributeError: 'Request' object has no attribute 'v3'
怎么个执行逻辑呢?
捕获到 return getattr(self._request, attr) 语句抛出的异常
(▽ 此异常是HttpRequest类中的__getattribute__中抛出的!!)
执行except体中的语句, self.__getattribute__(attr) 该语句就是单纯的执行 __getattribute__方法
Ps: `因为单纯,所以不等于点语法的逻辑,不存在,就不涉及到执行getattr方法.`
(此异常是Request类中的__getattribute__中抛出的!!)
"""
def __getattr__(self, attr):
try:
return getattr(self._request, attr) # 进行了伪装!!
except AttributeError:
return self.__getattribute__(attr) # 没有就直接报错.(能执行到这一步,attr肯定是没有的)
request = HttpRequest()
request.v1()
request.v2()
request = Request(request, 111)
request._request.v1()
request._request.v2()
# 伪装了一下,看似v1是request的成员,实则是request._request的成员!! crazy.
request.v1()
request.v2()
"""
v1
v2
v1
v2
v1
v2
"""
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# drf的request对象
在前面的博文中也有对此的阐述..
- 通过 from rest_framework.views import APIView 定位到类APIView
- 找到该类中的 dispatch 方法 (直接在视图函数的类的get方法中写 self.dispatch,跳转过去即可!!)
该方法中有这样两行代码
# 该行代码往上的request是Django中的request,该行代码往下的request是drf中的request!!
request = self.initialize_request(request, *args, **kwargs)
self.request = request
- 找到APIView类中的initialize_request方法!!
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
parser_context = self.get_parser_context(request)
# 可以看到,Django中的request作为参数传递给了drf中的Request类!!
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
- 跳转到drf的Request类
1> 可以看到在__init__方法中,有这样一行代码 self._request = request
2> Request类中还有__getattr__方法
def __getattr__(self, attr):
"""
If an attribute does not exist on this instance, then we also attempt
to proxy it to the underlying HttpRequest object.
"""
try:
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
3> Request类里还有个方法可以注意下!
@property
def query_params(self):
"""
More semantically correct name for request.GET.
"""
return self._request.GET
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 模拟
用python代码模拟CBV源码执行过程
class View:
@classmethod
def as_view(cls):
def view(request, *args, **kwargs):
self = cls()
return self.dispatch(request, *args, **kwargs)
return view
def dispatch(self, request, *args, **kwargs):
pass
class APIView(View):
@classmethod
def as_view(cls):
# view = super(APIView, cls).as_view()
view = super().as_view()
return view
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
handler = getattr(self, request.method.lower())
return handler(request, *args, **kwargs)
if __name__ == '__main__':
import random
class DjangoRequest:
def __init__(self):
self.method = random.choice(['GET', 'POST']) # 模拟发送的请求类型
class InfoView(APIView):
def get(self, request, xx):
print(self.args, self.kwargs) # (111,) {}
return "InfoView-GET"
def post(self, request, xx):
print(self.args, self.kwargs) # (111,) {}
return "InfoView-POST"
request = DjangoRequest()
print(InfoView.as_view()(request, 111)) # 111是模拟路由参数传递!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45