CBV
一. CBV源码解析
1.基于类的视图:因此必须要继承它:
from django.views import View
2.源码分析
class View:
# http_method_names : 存放了我们常用的请求方式
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
# 绑定给类的函数,绑定方法
@classonlymethod
def as_view(cls, **initkwargs):
# **initkwargs : 可变长关键字参数
for key in initkwargs:
# print(f"key :>>>> {key}") # key :>>>> pattern_name
# 可变长关键字参数中的每一个键值对
# 我们自己写视图类中没有定义过 http_method_names ,只能从父类 View 里面找
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__)
)
# hasattr : 获取到当前对象中的属性
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))
# 上面都没有报错,走到了 view 函数里面
def view(request, *args, **kwargs):
# 获取到当前类的对象 这个 cls ---> Register
self = cls(**initkwargs)
# 获取到了一个 当前的get方法
self.setup(request, *args, **kwargs)
# 必须接受一个位置参数叫request
if not hasattr(self, 'request'):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
# 触发了 dispatch 方法 ---> 获取到了get函数的返回值
# render
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
def setup(self, request, *args, **kwargs):
# self : 当前Register实例化出来的对象
# 有GET方法 并且 没有 head属性
if hasattr(self, 'get') and not hasattr(self, 'head'):
# self.head 变成了我自己写的 GET 方法
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
# 走到了dispatch方法
def dispatch(self, request, *args, **kwargs):
# request.method.lower() :当前请求方法转全小写
if request.method.lower() in self.http_method_names:
# 获取到了当前对象中的get方法的函数内存地址
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
# get方法的函数内存地址调用
return handler(request, *args, **kwargs)
def http_method_not_allowed(self, request, *args, **kwargs):
logger.warning(
'Method Not Allowed (%s): %s', request.method, request.path,
extra={'status_code': 405, 'request': request}
)
return HttpResponseNotAllowed(self._allowed_methods())
def options(self, request, *args, **kwargs):
"""Handle responding to requests for the OPTIONS HTTP verb."""
response = HttpResponse()
response.headers['Allow'] = ', '.join(self._allowed_methods())
response.headers['Content-Length'] = '0'
return response
def _allowed_methods(self):
return [m.upper() for m in self.http_method_names if hasattr(self, m)]
二. CBV添加装饰器
2.1 fbv 加装饰器
def timer(func):
def inner(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs)
print(f"总耗时 :>>>>> {time.time() - start_time} s ")
return res
return inner
@timer
def register(request):
time.sleep(2)
return HttpResponse("ok")
2.2 cbv 加装饰器
""" 方式一 """
class Login(View):
@timer
def get(self, request, *args, **kwargs):
time.sleep(2)
return HttpResponse("login")
""" 方式二 : 借助Django内置的公共函数 method_decorator """
from django.utils.decorators import method_decorator
@method_decorator(timer)
def get(self, request, *args, **kwargs):
time.sleep(2)
return HttpResponse("login")
""" 方式三:重写 dispatch 方法做拦截 """
def get(self, request, *args, **kwargs):
time.sleep(2)
return HttpResponse("login")
def dispatch(self, request, *args, **kwargs):
start_time = time.time()
# 可以什么都不写
# obj = super().dispatch(request, *args, **kwargs)
# 可以放自己的类名和自己的对象 self
obj = super(Login, self).dispatch(request, *args, **kwargs)
print(f"总耗时 :>>>> {time.time() - start_time} s ")
return obj
# 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)
3. CBV 基本使用
3.1 视图 views.py
from django.http import JsonResponse
from rest_framework.serializers import ModelSerializer
from django.views import View
from books.models import Book
# 序列化类
class BookSer(ModelSerializer):
class Meta:
model = Book # 跟Book做对应
fields = '__all__'
# Create your views here.
# 视图类
class BookView(View):
def get(self, request):
books = Book.objects.all()
data = BookSer(books, many=True)
return JsonResponse(data.data, safe=False)
def post(self, request):
print(request)
return JsonResponse('add book', safe=False)
3.2 路由 url.py
from django.urls import path
from books.views import BookView
urlpatterns = [
# 这里对 as_view() 不用进行任何操作, 因为视图层中继承的是 View。
path('book/', BookView.as_view())
]