django为视图添加装饰器

2018-10-30  本文已影响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'

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

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

from .views import my_decortator
urlpatterns = [
    url(r'^demo/$', my_decorate(DemoView.as_view()))  # DemoView.as_view()的结果是view方法  --> my_decorate(view)
]
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')

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

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

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')


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

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扩展类

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')

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

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

上一篇下一篇

猜你喜欢

热点阅读