Самый чистый способ украсить способ отправки Джанго - PullRequest
0 голосов
/ 28 мая 2018

Я наткнулся на код, который используется для предоставления некоторых аргументов в метод запроса.Проблема в том, что я не уверен, является ли это самым чистым способом в этом случае.

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

Есть ли проблема, если "я" не передано в определении метода здесь ...

        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, ...] в качестве атрибута класса.

У меня проблема иногда (я не могу воспроизвести ошибку ...), это то, что я получил KeyError на дополненных kwargs, в то время как определение URL установлено правильно.Например:

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

Я искал часы, и кажется, что у меня никогда не будет решения ... Это немного расстраивает.

Если кто-то может помочь, это будет оценено.

1 Ответ

0 голосов
/ 29 мая 2018

У меня есть догадка, что неправильная обработка атрибутов класса является ошибкой.


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.

...