django为视图添加装饰器

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

# 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扩展类,也会为类视图的所有请求方法都添加装饰行为。注意: 是类视图中的所有请求, 也就意味着它不能单一控制, 这也算是一个缺点吧
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容