美文网首页
django为视图添加装饰器

django为视图添加装饰器

作者: 陈卧虫 | 来源:发表于2018-10-30 00:20 被阅读0次

    如果想对一个视图功能进行扩展, 就需要使用到装饰器了, 但是如果通过普通的方法添加装饰器却不能得到想要的结果, 举个例子

    # decorator
    def my_decorator(func):
        def wrapper(self, request, *args, **kwargs):
            print('自定义装饰器被调用了')
            print('请求路径%s' % request.path)
            return func(self, request, *args, **kwargs)
        return wrapper
    
    # view
    class DemoView(View):
        
        @my_decorator
        def get(self, request):
            print('get方法')
            return HttpResponse('ok')
    
        def post(self, request):
            print('post方法')
            return HttpResponse('ok')
    

    但是运行以后, 结果可能是这样的

    AttributeError: 'Demoview' object has no attribute 'path'
    

    为什么会这样能? 这个后面就会解释. 所以说通过普通的方法进行装饰时没有办法解决问题,只能用其它的方法

    第一种: 在定义的路径当中手动添加装饰行为

    • urls.py
    from .views import my_decortator
    urlpatterns = [
        url(r'^demo/$', my_decorate(DemoView.as_view()))  # DemoView.as_view()的结果是view方法  --> my_decorate(view)
    ]
    
    • views.py
    def my_decorator(func):
        def wrapper(request, *args, **kwargs):
            print('自定义装饰器被调用了')
            print('请求路径%s' % request.path)
            return func(request, *args, **kwargs)
        return wrapper
    
    class DemoView(View):
        def get(self, request):
            print('get方法')
            return HttpResponse('ok')
    
        def post(self, request):
            print('post方法')
            return HttpResponse('ok')
    

    这种方法的实现原理是在视图的入口处添加装饰器:

    • as_view()的执行结果加装饰器, 由于as_view()方法返回的是一个view函数 ,在调用view函数之前进行装饰, 就相当于给每一个视图添加了装饰器, 因为它是一个视图的入口, 所以每次进入view函数之前都会调用一次!
    • 需要注意的是这种方式是手动的! 是根据装饰器的原理去实现手动装饰, 所以有点难以读懂
    • 它的装饰时对类视图里的所有方法, 不能进行单一控制
    • 装饰的位置是在urls.py中

    第二种: 在请求方法调用前添加装饰器

    • 为全部请求方法添加装饰器
    from django.utils.decorators import method_decorator
    
    # 为全部请求方法添加装饰器
    class DemoView(View):
    
        @method_decorator(my_decorator)  # 通过给dispatch方法添加装饰器实现
        def dispatch(self, *args, **kwargs):
            return super().dispatch(*args, **kwargs)
    
        def get(self, request):
            print('get方法')
            return HttpResponse('ok')
    
        def post(self, request):
            print('post方法')
            return HttpResponse('ok')
    
    
    • 为特定请求方法添加装饰器
    class DemoView(View):
    
        @method_decorator(my_decorator) # 为get方法添加
        def get(self, request):
            print('get方法')
            return HttpResponse('ok')
    
        def post(self, request):
            print('post方法')
            return HttpResponse('ok')
    
    
    

    它的原理: 在进入视图之前添加装饰器

    • 全部请求: 在进入dispatch方法之前
      • 其实和第一种方法差不多, 因为也是入口处添加装饰器, 只是比第一种方法装饰的时刻更晚, 是在view方法之后, 在调用dispatch方法之前添加装饰器.
      • 与第一种的区别在于, 它可以在view函数里修改, 而不用修改urls.
    • 特定请求: 在特定视图上用 @方法
    • 注意: 必须要使用method_decorator(my_decorator), 不能直接加my_decorator

    method_decorator装饰器还支持使用name参数指明被装饰的方法

    • 其实就是上面的简写
    # 为全部请求方法添加装饰器
    @method_decorator(my_decorator, name='dispatch')
    class DemoView(View):
        def get(self, request):
            print('get方法')
            return HttpResponse('ok')
    
        def post(self, request):
            print('post方法')
            return HttpResponse('ok')
    
    
    # 为特定请求方法添加装饰器
    @method_decorator(my_decorator, name='get')
    class DemoView(View):
        def get(self, request):
            print('get方法')
            return HttpResponse('ok')
    
        def post(self, request):
            print('post方法')
            return HttpResponse('ok')
    

    为什么需要使用method_decorator???

    首先来看一下装饰器所接受的参数

    def my_decorate(func):
        def wrapper(request, *args, **kwargs):  # 第一个参数request对象
            ...代码省略...
            return func(request, *args, **kwargs)
        return wrapper
    

    装饰器需要的第一个参数是 request对象, 但我们调用类视图中请求方法时, 传入的第一个参数不是request对象,而是self 视图对象本身,第二个位置参数才是request对象: self, request, *args, **kwargs,

    所以如果直接将用于函数视图的装饰器装饰类视图方法,会导致参数传递出现问题。

    method_decorator的作用是为函数视图装饰器补充第一个self参数,以适配类视图方法。

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

    补充上self以后, 问题完美解决!

    第三种: 构造Mixin扩展类

    • 原理:重写as_view()方法, 在进入返回view函数之前, 对view函数进行装饰
      • 和第二种的差不多, 第二种是重写加扩展dispatch方法
      • 第三种是重写加扩展as_view方法
    class MyDecoratorMixin(object):
        @classmethod
        def as_view(cls, *args, **kwargs):
            view = super().as_view(*args, **kwargs)  # 调用as_view(),返回view方法
            view = my_decorator(view)  # 对view方法进行装饰
            return view  # 返回这个装饰过的view方法
    
    class DemoView(MyDecoratorMixin, View):  # 通过多继承的原理, 视图继承重写后的as_view
        def get(self, request):
            print('get方法')
            return HttpResponse('ok')
    
        def post(self, request):
            print('post方法')
            return HttpResponse('ok')
    

    总结: 其实功能和第二种差不多, 那为什么还用第三种呢, 因为它更加通用, 它最大的优点是 能给 多个不同的类视图进行装饰, 它不局限于只能给某个单一的视图添加!

    而第一,二种都是给单一的类视图添加, 不具备通用性, 所以在开发中第三种往往更加通用.

    • 使用Mixin扩展类,也会为类视图的所有请求方法都添加装饰行为。注意: 是类视图中的所有请求, 也就意味着它不能单一控制, 这也算是一个缺点吧

    相关文章

      网友评论

          本文标题:django为视图添加装饰器

          本文链接:https://www.haomeiwen.com/subject/qykwtqtx.html