在django中视图有两种写法,一种是FBV,另外一种是CBV。
FBV是function based view的意思,就是说view中都是定义的函数来实现的功能,而CBV是class based view的意思,就是说在view中都是定义的类,使用类的方法来实现功能。
举个例子,应该也是最典型的例子。首先先建一个最基本的django框架,然后开始来做FBV和CBV的对比。
markdown
drf_study/
├── apps/
│ └── case01/
├── drf_study/
│ ├── __pycache__/
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── manage.py
└── requirements.txt
FBV和CBV在两个地方有区别,一个是路由上,一个是视图上。
在路上有,我先在urls
上分别定一个FBV
路由和CBV
路由,来看一下区别。
python
# drf_study/urls.py
from django.urls import path
from apps.case01 import views
urlpatterns = [
path('fbv/', views.fbv), # FBV路由,指向一个函数,当路由进来的时候,就执行这个函数
path('cbv/', views.CbvViews.as_view()), # CBV路由,指向as_view的返回结果
]
再来看一下视图上的区别:
python
# drf_study/case01/views.py
from django.http import HttpResponse
from django.views import View # 要用CBV,一定要导入这个类,所有的CBV中的C都继承自这个类
def fbv(request):
return HttpResponse("<h1>this is fev</h1>")
class CbvViews(View):
# 如果是GET请求就执行下面的函数
def get(self, request):
return HttpResponse("<h1>this is CBV</h1>")
上面的两个请求还是有一些区别,在FBV中,任何请求都会返回,而在CBV中只有GET
请求才会返回内容,如果是POST的请求,它不会返回,因为在CbvView
这个类中没有定义POST请求的调用方法。
如果要让FBV中也达到CBV中的效果,那么需要在FBV的函数中加上一个判断才可以。
python
# drf_study/case01/views.py
from django.http import HttpResponse
from django.views import View # 要用CBV,一定要导入这个类,所有的CBV中的C都继承自这个类
def fbv(request):
if request.method == 'GET':
return HttpResponse("<h1>this is fev</h1>")
else:
return HttpResponse("<h1>this is not get method</h1>")
class CbvViews(View):
# 如果是GET请求就执行下面的函数
def get(self, request):
return HttpResponse("<h1>this is CBV</h1>")
那么为什么CBV的路由中返回的as_view
的调用结果,就是去执行CbvViews
类中定义的get
函数呢?
在CBV的路由中,访问cbv/
的时候,按照FBV的逻辑,在路由中对应的是一个函数引用,访问过来就去执行它。但是在CBV中它对应的是views.CbvViews.as_view()
,而在视图views
中写的CbvViews
中并没有这个as_view()
方法,所以python会去CbvViews
的__mro__
中的下一个对象中去找,而下一个对象就是它的直接父类django中的views
模块中的View
类。
在django的views
模块的View
类中,找到了这个as_view()
,这个函数返回的是as_view
中定义的一个view
,是一个闭包,类似于下面这个。
python
def as_view(cls, **initkwargs):
def view(request, *args, **kwargs):
...
return self.dispatch(request, *args, **kwargs)
.....
return view
所以在cbv/
正式来的时候,就会去执行这个闭包中的view()
,而这个闭包中的view()
返回的是self.dispatch(request, *args, **kwargs)
的结果,而这个self
就是我们自己定义的CbvViews
,而CbvViews
我并没有定义dispatch
方法,于是python又会去它的__mro__中下一个对象django中的views
模块中的View
类中去找。
在View中找到了这个dispatch方法,这个执行结果返回的是handler(request, *args, **kwargs)
的返回结果。
python
def dispatch(self, request, *args, **kwargs):
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)
那么来看handler
函数又是什么?handler
是request.method.lower()
,比如我们是用GET方法访问的,那么这里handler
就是get
,返回的handler(request, *args, **kwargs)
就是get(request, *args, **kwargs)
,而get
函数正好在我定义的CbvViews
中是有的,于是就去执行CbvViews
中定义的get
函数了。
总结一下,所以实际上在django中不管是FBV还是CBV,路由过来了,都是去找到那个函数,并且执行它,只不过CBV在找函数的时候绕了很多的弯。
