首页 > 解决方案 > 装饰 Django 调度方法的最简洁方法

问题描述

我偶然发现了一个用于为请求方法提供一些参数的代码。问题是我不确定这是否是处理这种情况的最干净的方法。

def check_permissions(check_mixins):
"""
:param check_mixins: is given to the inner decorator
Decorator that will automatically populate some parameters when
using dispatch() toward the right method (get(), post())

"""
  def _decorator(_dispatch):
    def wrapper(request, *args, **kwargs):

如果这里的方法定义中没有传递“self”会不会有问题...

        for mixin in check_mixins:
            kwargs = mixin.check(request, *args, **kwargs)
            if isinstance(kwargs, HttpResponseRedirect):
                return kwargs

        return _dispatch(request, *args, **kwargs)

    return wrapper

  return _decorator


class UserLoginMixin(object):

def check(request, *args, **kwargs):

... 和这里 ?在我的 IDE 中看起来很丑

    user = request.user
    if user.is_authenticated() and not user.is_anonymous():
        kwargs['user'] = user
        return kwargs
    return redirect('user_login')

class AppoExistMixin(object):

def check(request, *args, **kwargs):

这里也...

    appo_id = kwargs['appo_id']
    try:
        appoff = IdAppoff.objects.get(id=appo_id)
        kwargs['appoff'] = appoff
        del kwargs['appo_id']
        return kwargs
    except IdAppoff.DoesNotExist:
        pass
    messages.add_message(request, messages.ERROR,
                         "Item doesn't exist!")
    return redirect('home')



class SecurityMixin(View):
"""
    Mixin that dispatch() to the right method with augmented kwargs.
    kwargs are added if they match to specific treatment.
"""

data = []

def __init__(self, authenticators):
    super(SecurityMixin, self).__init__()
    # Clearing data in order to not add useless param to kwargs
    self.data.clear()
    # Build the list that contain each authenticator providing
    # context increase
    for auth in authenticators:
        self.data.append(auth)

@method_decorator(check_permissions(data))

为什么是 data 而不是 self.data ?这怎么可能 ?

def dispatch(self, request, *args, **kwargs):
    return super(SecurityMixin, self).dispatch(request, *args, **kwargs)

然后每个视图都从SecurityMixin继承并authenticators = [UserLoginMixin, ...]作为类属性获取。

有时遇到的问题(我无法重现错误......)是在正确设置 URL 定义时,我在增强的 kwargs 上遇到了 KeyError。例如:

appo_id = kwargs['appo_id']
KeyError: 'appo_id'Exception

我一直在寻找几个小时,似乎我永远不会有解决方案......这有点令人沮丧。

如果有人可以提供帮助,将不胜感激。

标签: djangopython-decorators

解决方案


我有一种预感,对类属性的不当处理是错误的。


类与实例

每次调用时都会覆盖类属性:dataSecurityMixin.__init__

class A:
    data = []

    def __init__(self, *args):
        self.data.clear() # self.data references the class attribute
        for x in args:
            self.data.append(x)

    x = A('foo')
    # A.data = ['foo']
    # x.data = ['foo']
    y = A('bar')
    # A.data = ['bar']
    # y.data = ['bar']
    # x.data = ['bar'] !!

然而:

class A:
    data = ['I am empty']

    def __init__(self, *args):
        self.data = [] # redeclaring data as an instance attribute
        for x in args:
            self.data.append(x)

    x = A('foo')
    # A.data = ['I am empty']
    # x.data = ['foo']
    y = A('bar')
    # A.data = ['I am empty']
    # y.data = ['bar']
    # x.data = ['foo'] 

此类属性被传递给装饰器(您不能将实例属性data传递给方法装饰器,即 self.data,因为在装饰器声明期间该实例尚不存在)。但是,如果传入('self' 参数),包装函数确实可以访问实例。

Djangomethod_decorator删除了这个 self 参数;该装饰器用于将函数装饰器(不self隐式获取参数)转换为方法装饰器(self隐式获取参数)。这就是为什么您不必self在各种 mixin 检查方法的参数列表中包含它,因为它已被method_decorator. 简单来说:用method_decorator函数装饰器来装饰方法。在此处阅读装饰 CBV的内容。

知道了这一点,我不确定为什么当你只使用它来装饰方法时,为什么check_permissions应该像现在这样成为一个函数装饰器。你可以check_permissions自己装饰调度:

def check_permissions(_dispatch):
    def _decorator(self, request, *args, **kwargs): # adding self
        for mixin in self.data: # referencing the INSTANCE data
            kwargs = mixin.check(request, *args, **kwargs) 
            if isinstance(kwargs, HttpResponseRedirect):
                return kwargs
        return _dispatch(self, request, *args, **kwargs) # don't forget self here
    return _decorator


@check_permissions
def dispatch(self, request, *args, **kwargs):
    ...

也许某些视图正在尝试检查AppoExistMixin,因为它在该视图的数据列表中,尽管它不应该是 - 并且视图的 kwargs 不包括“appo_id”。您也可以通过将所需的检查混合直接传递给装饰器来尝试显式:@method_decorator(check_permissions([UserLoginMixin, ...])). 这样您就不必弄乱类与实例属性。

另外......你应该重命名data为你不可能用你自己的变量覆盖的东西。

如果你想变得超级懒惰,你可以这样做:

appo_id = kwargs.get('appo_id',False)
if not appo_id: return kwargs

但这只会修复该视图中的特定错误。它忽略了症状而不是治愈疾病。


更多解释:

功能与方法。check_permissions函数,whiledispatch()方法。您不能简单地在方法上使用函数装饰器:一方面,因为隐式参数(方法所属self的实例)也会传递给装饰器,尽管它可能不会期望它。 这就是 django通过在装饰器中删除和存储而发挥作用的地方。比较两个签名:vs 。在前者中,在调用函数装饰器之前“吸收” 。把它想象成一个适配器,一个装饰器的装饰器,它“弥合了函数和方法之间的鸿沟”。如果您不想/不能更改装饰器,请使用它。
method_decoratorselfwrapper(request, *args, **kwargs)_decorator(self, request, *args, **kwargs)method_decoratorself
但是,在您的情况下,您可以更改装饰器以使其与方法一起使用 - 因此您不需要 django 的method_decorator.


推荐阅读