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())
]
posted @ 2023-04-03 20:54  codegjj  阅读(3)  评论(0编辑  收藏  举报