一、简介
本文章首先通过使用CBV和FBV两种不同的编写方式实现相同功能后,通过介绍as.view()的源码来介绍CBV的本质
二、代码
①项目目录结构
②view.py
from django.http import JsonResponse
from django.views import View
# FBV
def auth(request):
# 如果请求方式为GET
if request.method == "GET":
return JsonResponse({'status': True, 'message': "GET"})
# 如果请求方式为POST
elif request.method == "POST":
return JsonResponse({'status': True, 'message': "POST"})
# 如果请求方式为其他
return JsonResponse({'status': True, 'message': "other"})
# CBV
class UserView(View):
def get(self, request):
return JsonResponse({'status': True, 'message': "GET"})
def post(self, request):
return JsonResponse({'status': True, 'message': "POST"})
def delete(self, request):
return JsonResponse({'status': True, 'message': "DELETE"})
def put(self, request):
return JsonResponse({'status': True, 'message': "PUT"})
③urls.py
from django.urls import path
from app01 import views
urlpatterns = [
# path('admin/', admin.site.urls),
path('auth/', views.auth), # FBV
path('user/', views.UserView.as_view()), # CBV
]
三、Postman测试API接口
首先需要将settings..py中的'django.middleware.csrf.CsrfViewMiddleware'注释掉(防止django做csrf校验)
①FBV方式
get请求
post请求
patch请求
②CBV方式
get请求
post请求
四、as_view()
通过测试接口,CBV和FBV都可实现相同的返回,表面的区别就在于FBV用的def定义函数,而CBV用的class定义类,且在urls.py中CBV在类名后面多跟了as_view()
通过url的定义,可以知道当访问/user/这个api接口时,会执行UserView中的as_view()的返回结果,重点就在于as.view(),弄清楚as.view()实现的具体功能,即可明白CBV的本质
path('user/', views.UserView.as_view()), # CBV
UserView
# django_CBV
class UserView(View):
def get(self, request):
return JsonResponse({'status': True, 'message': "GET"})
def post(self, request):
return JsonResponse({'status': True, 'message': "POST"})
def delete(self, request):
return JsonResponse({'status': True, 'message': "DELETE"})
def put(self, request):
return JsonResponse({'status': True, 'message': "PUT"})
从上面代码可知,UserView中并没有as_view,因此需要进入父类View中寻找
View中的as_view
@classonlymethod
def as_view(cls, **initkwargs):
"""Main entry point for a request-response process."""
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError(
'The method name %s is not accepted as a keyword argument '
'to %s().' % (key, cls.__name__)
)
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key))
def view(request, *args, **kwargs):
self = cls(**initkwargs)
self.setup(request, *args, **kwargs)
if not hasattr(self, 'request'):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view
先看返回值,返回了一个view,而view在as_view中已经定义了,因此是一个闭包函数,于是看定义的view
①实例化了一个类对象,这里的cls是指UserView类
self = cls(**initkwargs)
②返回self.dispatch,即UserView中的dispatch方法,显然在UserView中没有定义该方法,因此仍然去父类View中寻找
return self.dispatch(request, *args, **kwargs)
③View中的dispatch
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
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
return handler(request, *args, **kwargs)
第一步是判断当前reques.method的小写是否在http_method_names中
http_method_names
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
第二步使用getattr反射出当前实例化对象UserView中与request.method对应的方法名称
即UserView.get,UserView.post,UserView.delete等
第三步返回handler(request, *args, **kwargs),即执行UserView.get(request, *args, **kwargs),UserView.post(request, *args, **kwargs)等
④as.view()本质
path('user/', views.UserView.as_view()), # CBV
因此views.UserView.as_view()的本质即根据request.method执行在UserView中定义的不同方法,如get,post,delete等
五、总结
了解as.view()的本质后,即可发现FBV和CBV的表面代码虽有不同,但其本质仍是一样,都是通过request.method的不同执行所相对应的代码块,只不过CBV的判断是由其继承的父类View中定义的as_view方法所实现,而FBV则是在自身定义的函数中实现的判断