关于Django类视图部分源码分解

Django类视图源码解析及dispatch函数

  • 类视图原理
    为什么我们定义url的时候, 调用 as_view() 函数,就可以达到结果, 如果不调用就会报错.

到底 as_view() 帮助我们干了什么了?

 @classonlymethod
    def as_view(cls, **initkwargs):

        ...省略代码...

        def view(request, *args, **kwargs):
            # 这里的cls是as_view这个函数接收的第一个参数,也就是调用当前函数的类.
            # 得到调用的类了之后, 创建类的对象: self
            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
            # 调用dispatch方法,按照不同请求方式调用不同请求方法
            return self.dispatch(request, *args, **kwargs)

        ...省略代码...

        # 返回真正的函数视图
        return view

    # dispatch本身是分发的意思,这里指的是函数的名字.
    def dispatch(self, request, *args, **kwargs):
        # self.http_method_names指的是我们的类视图中,对象方法的名字
        # 这里把所有方法的名字都存放在了http_methods_names中
        # 我们会把当前请求的方式转为小写,然后判断是否在列表中存在.
        if request.method.lower() in self.http_method_names:
            # 如果在里面, 则进入这里
            # 这里的getattr作用是获取当前对象的属性.
            # 下面的参数为: 
            # self :  类视图对象
            # request.method.lower() : 请求方法的小写. 例如: 'get' 或 'post'
            # http_method_not_allowed : 后续处理方式(不允许请求)
            # 下面代码整体的意思: 根据类视图对象, 获取当前类视图中对应名称的方法
            # 如果获取到, 则把方法返回给handle, 否则不允许访问.
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            # 如果类视图中如果没有的话, 则进入这里, 表明不允许进行请求.
            # 我们会把不允许请求这个字段返回给handle.
            handler = self.http_method_not_allowed
        # 最终返回handle(handle里面要么包含可以访问的方法, 要么就是不允许访问的字段)
        return handler(request, *args, **kwargs)
  • 类视图使用装饰器

在处理正常项目需求时, 装饰器修会在极大程度上方便我们的工作,避免重复造轮子. 那么, 如果我们定义了一个类视图, 它是否可以使用装饰器进行修饰呢?

为了理解方便,我先来定义一个为函数视图准备的装饰器(在设计装饰器时基本都以函数视图作为考虑的被装饰对象),以及一个要被装饰的类视图, 来说明一下给类添加装饰器的实现要点.


#  定义一个装饰器
#  func = my_decorator(func) 
def my_decorator(func):
    # wrapper函数必然会接收一个request对象,因为传入进来的func这个函数肯定会带这个参数
    def wrapper(request, *args, **kwargs):
        print('自定义装饰器被调用了')
        print('请求路径%s' % request.path)
        return func(request, *args, **kwargs)
    return wrapper

# 我们定义的类视图
class DemoView(View):
    # 我们给get方法添加上装饰器, 然后执行.
    @my_decorator
    def get(self, request):
        print('get方法')
        return HttpResponse('ok')

    # 类视图里面的对象方法: post方法
    def post(self, request):
        print('post方法')
        return HttpResponse('ok')

演示效果

结论:

我们会发现上面写的代码出错了, 错误原因就是我们定义的装饰器是修饰视图函数的, 而不是修饰类视图的, 所以使用装饰器来强行修饰, 会有问题.

那么我们怎样才能把一个修饰函数的装饰器作用在类上呢?

我们可以思考一下得到: url部分我们其实是有使用 as_view( ) 函数的, 而这个函数最终会给我们返回一个view( ) 函数, 换句话说: 我们在调用类视图之前,必然会调用 view( ) 这个函数, 那我们是否可以把装饰器作用到 view( ) 这个函数上去呢

  • 修饰dispatch函数, 实现多个函数装饰
from django.views.generic import View
from django.http import HttpResponse
from django.utils.decorators import method_decorator

# 自定义的装饰器方法
def my_decorator(func):
    def wrapper(request, *args, **kwargs):
        print('自定义装饰器被调用了')
        print('请求的路径:%s' % request.path)
        return func(request, *args, **kwargs)
    return wrapper

# 类视图
class DemoView(View):
    # 重写父类的dispatch方法, 因为这个方法被 as_view() 中的 view() 调用
    # 所以我们对这个方法添加装饰器, 也就相当于对整个类视图的方法添加装饰器.
    @method_decorator(my_decorator)
    def dispatch(self, request, *args, **kwargs):
        # 重写父类的这个方法我们不会修改它的任何参数, 所以我们直接调用父类的这个方法即可
        # 它里面的参数我们也不动它, 直接还传递过去.
        return super().dispatch(request, *args, **kwargs)

    def get(self, request):
        print('get')
        return HttpResponse('getfunc ok')

    def post(self, request):
        print('post')
        return HttpResponse('postfunc ok')

展示效果 :
在这里插入图片描述

  • 上式可以变形为: (扩展)
from django.views.generic import View
from django.http import HttpResponse
from django.utils.decorators import method_decorator

def my_decorator(func):
    def wrapper(request, *args, **kwargs):
        print('自定义装饰器被调用了')
        print('请求的路径:%s' % request.path)
        return func(request, *args, **kwargs)
    return wrapper

# 类视图
# 给类视图增加上@method_decorator方法
# 增加上之后,需要配置第二个参数, 否则没有任何装饰行为
# 所以我们需要给method_decator配置第二个参数
# 指定把装饰器添加到某个函数上. 
@method_decorator(my_decorator, name='dispatch')
class DemoView(View):
    def get(self, request):
        print('get')
        return HttpResponse('getfunc ok')

    def post(self, request):
        print('post')
        return HttpResponse('postfunc ok')

结论:

我们发现, 经过中间一层额外扩展类的装饰过滤, 我们原来的DemoView中的所有视图方法是能够经过装饰器的.

我们可以定义两个扩展类, 并且重写两次 as_view 方法

from django.views.generic import View
from django.http import HttpResponse
from django.utils.decorators import method_decorator

# 定义的第一个装饰器: 
def my_decorator(func):
    def wrapper(request, *args, **kwargs):
        print('自定义装饰器被调用了')
        print('请求的路径:%s' % request.path)
        return func(request, *args, **kwargs)
    return wrapper

# 定义的第二个装饰器: 
def my_decorator2(func):
    def wrapper(request, *args, **kwargs):
        print('自定义装饰器被调用了22222')
        print('请求的路径:%s' % request.path)
        return func(request, *args, **kwargs)
    return wrapper

# 额外增加的第一个扩展类
class BaseView(View):
    # 第一次重写父类中的as_view方法
    @classmethod
    def as_view(cls, *args, **kwargs):
        view = super().as_view(*args, **kwargs)
        # 对获取的view第一次添加装饰器
        view = my_decorator(view)
        return view

# 额外增加的第二个扩展类
class Base2View(View):
    # 第二次重写父类中的as_view方法
    @classmethod
    def as_view(cls, **initkwargs):
        view = super().as_view(**initkwargs)
        # 对获取的view进行第二次添加装饰器
        view = my_decorator2(view)
        return view

# 我们定义的类视图, 继承自两个额外增加的类
class DemoView(BaseView, Base2View):
    # 类视图中的get方法
    def get(self, request):
        print('get')
        return HttpResponse('get func')

    # 类视图中的post方法
    def post(self, request):
        print('post')
        return HttpResponse('post func')

说明:

如果两个扩展类的父类相同: 则两个父类都会调用
如果两个扩展类的父类不同: 则只会调用第一个父类

# 第一个扩展类, 让他继承自object
class BaseView(object):
    @classmethod
    def as_view(cls, *args, **kwargs):
        view = super().as_view(*args, **kwargs)
        view = my_decorator(view)
        return view

# 第二个扩展类,让他继承自object
class Base2View(object):
    @classmethod
    def as_view(cls, *args, **kwargs):
        view = super().as_view(*args, **kwargs)
        view = my_decorator2(view)
        return view

# 类视图, 让他除了继承自这两个父类外, 最后继承View类.
class DemoView(BaseView, Base2View, View):
    def get(self, request):
        print('get方法')
        return HttpResponse('ok')

    def post(self, request):
        print('post方法')
        return HttpResponse('ok')

说明:

因为都是继承自object,所以扩展类中的super.as_view都会去找其他的父类依次执行,最终都会执行到View这个类这里, 所以肯定会执行View中的as_view方法

使用Mixin扩展类,也会为类视图的所有请求方法都添加装饰行为!!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值