У меня есть догадка, что неправильная обработка атрибутов класса является ошибкой.
CLASS VS INSTANCE
Атрибут class 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']
Этот атрибут class data
передается декоратору(Вы не можете передать атрибут экземпляра декоратору метода, то есть self.data, потому что экземпляр еще не существует во время объявления декоратора).Обернутая функция, однако, имеет доступ к экземпляру, если он передан (аргумент 'self').
Джанго method_decorator
удаляет этот аргумент self;этот декоратор используется для преобразования декоратора function (который не получает аргумент self
неявно) в декоратор method (который неявно получает параметр self
).Вот почему вам не нужно включать self
в список параметров для различных методов проверки mixin, так как он был удален с помощью method_decorator
.Проще говоря: используйте method_decorator
, чтобы украсить метод с помощью функции 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
- это функция , а dispatch()
- это метод .Вы не можете просто использовать декоратор функции для метода: например, неявный аргумент self
(экземпляр, к которому относится метод ) также передается декоратору, хотя он может этого не ожидать.
Вот где в Django входит method_decorator
, удаляя и сохраняя self
в декораторе.Сравните две подписи: wrapper(request, *args, **kwargs)
против _decorator(self, request, *args, **kwargs)
.В первом случае method_decorator
'поглощается' self
до вызова функции декоратора.Думайте об этом как об адаптере, декораторе для декоратора, который «ликвидирует разрыв» между функцией и методом.Используйте его, если вы не хотите / не можете изменить декоратор.
В вашем случае, однако, вы можете изменить декоратор, чтобы он работал с методом - таким образом, вам не нужны djangomethod_decorator
.