CBV(class base views) 就是在视图里使用类处理请求。
Python是一个面向对象的编程语言,如果只用函数来开发,有很多面向对象的优点就错失了(继承、封装、多态)。所以Django在后来加入了Class-Based-View。可以让我们用类写View。这样做的优点主要下面两种:
写一个简单CBV视图:
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^login/', views.LoginView.as_view()),
]
app01下的views.py
from django.views import View
from django.shortcuts import render,HttpResponse
class LoginView(View):
def get(self,request):
return render(request,"login.html")
def post(self,request):
user=request.POST.get("user")
pwd=request.POST.get("pwd")
if 1:
return HttpResponse("OK")
当我们在浏览器中发送post或get的url请求,就可以进入视图中的得到请求的响应内容。这个过程我们需要从源码中进行深入了解。
url调用三步走:
url(r'^login/', views.LoginView.as_view()),
url(r'^login/', View.as_view()),
url(r'^login/', View.view),
Django中View的原码:
class View(object):
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
def __init__(self, **kwargs):
for key, value in six.iteritems(kwargs):
setattr(self, key, value)
@classonlymethod
def as_view(cls, **initkwargs):
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (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)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
'''要知道此时的self是谁,才可理顺下一步持行方向。如果View的子类中有定义了dispatch方法就用子类的,如果没有就用View的dispatch方法。'''
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
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_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
Django的url是将一个请求分配给可调用的函数,CBV提供了一个as_view()
类方法,调用这个方法,会创建一个类的实例,然后通过实例调用dispatch()
方法,dispatch()
方法会根据request的method的不同调用相应的方法来处理request(如get()
,post()
等)。到这里,这些方法和function-based view差不多了,要接收request,得到一个response返回。如果方法没有定义,会抛出HttpResponseNotAllowed异常。
APIView
是REST framework提供的所有视图的基类,继承自Django的View
父类。
APIView与View的不同之处在于:
Request
对象,而不是Django的HttpRequeset
对象;Response
对象,视图会为响应数据设置(render)符合前端要求的格式;APIException
异常都会被捕获到,并且处理成合适的响应信息;我们来写一个小例子了解一下它的执行流程。
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^authors/',views.Authors_view.as_view()),
]
app01下的views.py
from django.shortcuts import render,HttpResponse
from app01.models import Author
from rest_framework.views import APIView
from django.core.serializers import serialize
class Authors_view(APIView):
def get(self,request):
ret=Author.objects.all()
b=serialize('json',ret)
return HttpResponse(b)
url调用的过程四步走:
url(r'^authors/',views.Authors_view.as_view()),
url(r'^authors/', APIView.as_view()),
url(r'^authors/', View.as_view()),
url(r'^authors/', View.view),
看一下REST framework中的APIView中的部分源码来分析其中的具体过程。
from django.views.generic import View
class APIView(View):
@classmethod
def as_view(cls, **initkwargs):
if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
def force_evaluation():
raise RuntimeError(
'Do not evaluate the `.queryset` attribute directly, '
'as the result will be cached and reused between requests. '
'Use `.all()` or call `.get_queryset()` instead.'
)
cls.queryset._fetch_all = force_evaluation
view = super(APIView, cls).as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs
return csrf_exempt(view)
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
self.initial(request, *args, **kwargs)
# Get the appropriate handler method
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
response = 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
从源码中我们看至APIView和Django中的View是继承的关系,它非富了View的工能。当我们使用APIView时我们需要了解它的代码执行流程。就以上面的例子为例。
/authors/
url请求路径的时候,就会走到url(r'^authors/',views.Authors_view.as_view()),
里面。Authors_view
类是继承了APIView
。所以我们要进入APIView里,发现APIView.as_view()
中有这样一行代码 view = super(APIView, cls).as_view(**initkwargs)
。View.as_view()
中,调用这个方法,会创建一个类的实例。return self.dispatch(request, *args, **kwargs)
时候,此时的self,不是View而是APIView.APIVIew.dspatch()
中执行分发。通过实例调用dispatch()
方法,根据request的method的不同调用相应的方法来处理request(如get()
,post()
等)。