最干净的方式来装饰Django的调度方法

时间:2018-05-28 15:38:33

标签: django python-decorators

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

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

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

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

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

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

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

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

如果有人可以提供帮助,我们将非常感激。

1 个答案:

答案 0 :(得分:0)

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

CLASS VS INSTANCE

每次调用data时都会覆盖属性SecurityMixin.__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'参数),则包装函数可以访问实例。

Django' method_decorator删除了这个自我论点;该装饰器用于将函数装饰器(它没有隐式获得self参数)转换为方法装饰器(获取self参数隐含地)。这就是为什么您不必在self删除各种mixin检查方法的参数列表中包含method_decorator的原因。简单地说:使用method_decorator用函数装饰器来装饰方法。请在此处阅读decorating CBVs

知道这一点,我不确定为什么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& #39 ;.您也可以通过将所需的check mixins直接传递给装饰器来尝试显式:@method_decorator(check_permissions([UserLoginMixin, ...]))。通过这种方式,您不必弄乱类和实例属性。

此外......您应该将data重命名为您不太可能使用自己的变量覆盖的内容。

如果你想成为超级懒人,你可以这样做:

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

但这只会修复那个视图中的特定错误。它忽视了一种症状,而不是治愈这种疾病。

更多解释:

功能与方法。 check_permissions函数,而dispatch()方法。你不能简单地在方法上使用函数装饰器:首先,因为隐式参数self方法所属的实例)也被传递给装饰器,尽管它可能不会期待它。
这就是django的method_decorator通过在装饰器中移除和存储self而进入的地方。比较两个签名:wrapper(request, *args, **kwargs) vs _decorator(self, request, *args, **kwargs)。在前者,method_decorator'吸收'调用函数装饰器之前self。 可以把它想象成一个适配器,一个装饰器的装饰器,它可以弥补差距。功能和方法之间。如果您不想/不能改变装饰者,请使用它 但是,在您的情况下,可以更改装饰器以使其与方法一起使用 - 因此您不需要django' method_decorator