Проверить имя параметра метода просмотра в представлениях на основе класса Django

Я создал декоратор в своем проекте Django для добавления значений параметров в параметры оформленного метода.

Я делаю это с помощью inspect.getargspec чтобы проверить, какие параметры присутствуют в методе, и поместить их в kwargs. В противном случае я получаю ошибку из-за неправильного количества параметров в методе.

Хотя это работает должным образом в отдельных методах представления, оно завершается ошибкой, когда дело касается представлений на основе классов Django.

Я полагаю, что это может быть потому, что декораторы применяются с помощью @method_decorator на уровне класса к методу dispatch а не к отдельным методам get и post.

Я новичок в Python и, возможно, пропускаю что-то очевидное здесь.

Есть ли лучший способ сделать то, что я делаю? Можно ли получить имена параметров метода в представлении на основе классов?

Я использую Python 2.7 и Django 1.11

Декоратор

def need_jwt_verification(decorated_function):
    @wraps(decorated_function)
    def decorator(*args, **kwargs):
        request = args[0]
        if not isinstance(request, HttpRequest):
            raise RuntimeError(
                "This decorator can only work with django view methods accepting a HTTPRequest as the first parameter")

        if AUTHORIZATION_HEADER_NAME not in request.META:
            return HttpResponse("Missing authentication header", status=401)

        jwt_token = request.META[AUTHORIZATION_HEADER_NAME].replace(BEARER_METHOD_TEXT, "")

        try:
            decoded_payload = jwt_service.verify_token(jwt_token)

            parameter_names = inspect.getargspec(decorated_function).args

            if "phone_number" in parameter_names or "phone_number" in parameter_names:
                kwargs["phone_number"] = decoded_payload["phone"]
            if "user_id" in parameter_names:
                kwargs["user_id"] = decoded_payload["user_id"]
            if "email" in parameter_names:
                kwargs["email"] = decoded_payload["email"]

            return decorated_function(*args, **kwargs)
        except JWTError as e:
            return HttpResponse("Incorrect or expired authentication header", status=401)

    return decorator

Представление на основе классов

@method_decorator([csrf_exempt, need_jwt_verification], name="dispatch")
class EMController(View):
    def get(self, request, phone_number, event_id):
        data = get_data()

        return JsonResponse(data, safe=False)

    def post(self, request, phone_number, event_id):


        return JsonResponse("Operation successful", safe=False)

РЕДАКТИРОВАТЬ:

Очевидное решение применения декоратора на уровне метода не работает с представлениями на основе классов Django. Вам необходимо применить декоратор в конфигурации URL или применить декоратор к методу отправки.

РЕДАКТИРОВАТЬ: я опубликовал код, который был связан с обходным путем, который я изучал, передавая имена параметров в качестве аргумента в декоратор.

Ответ 1

То, что я хочу, казалось невозможным в текущем состоянии библиотек. Итак, вот что я, наконец, пошел.

            parameter_names = inspect.getargspec(decorated_function).args

            if "phone_number" in parameter_names or "phone_number" in injectables:
                kwargs["phone_number"] = decoded_payload["phone"]
            if "user_id" in parameter_names:
                kwargs["user_id"] = decoded_payload["user_id"]
            if "email" in parameter_names:
                kwargs["email"] = decoded_payload["email"]

            request.__setattr__("JWT", {})
            request.JWT["phone_number"] = decoded_payload["phone"]
            request.JWT["user_id"] = decoded_payload["user_id"]
            request.JWT["email"] = decoded_payload["email"]

Этот декоратор автоматически заполняет параметры в представлениях, основанных на методе, как предполагалось.

Но он также добавит атрибут JWT к объекту запроса для использования видов, основанных на классе. Как request.GET и request.POST.

Ответ 2

Я нашел этот пост: Декораторы функций с параметрами на представлении класса в Django

который может дать ответ на вашу проблему:

Если вы хотите передать декоратор с параметрами, вам нужно только:

  • Оцените параметры в функции создателя-декоратора.

  • Передайте оцениваемое значение @method_decorator.

Вышеупомянутый и код, приведенный в связанном ответе, который был рассмотрен, вы должны:

injectables=[inject_1, inject_2, ..., inject_n]
decorators = [csrf_exempt, need_jwt_verification(injectables)]

@method_decorator(decorators, name="dispatch")
class EMController(View):
    ...


Оставив мой предыдущий ошибочный ответ здесь по причинам, связанным с наследием, не пробуйте это дома (или где угодно, в джанго, если на то пошло!!)

Если мы наблюдаем "decorating class" docs, мы можем видеть следующее:

Или, более кратко, вы можете украсить класс вместо и передать имя метода, который будет оформлен как имя аргумента ключевого слова:

поэтому вам нужно изменить аргумент name вашего @method_decorator, чтобы он соответствовал методу, который будет применяться к:

decorators = [csrf_exempt, need_jwt_verification(injectables=[])]

@method_decorator(decorators, name='get')
@method_decorator(decorators, name='post')
class EMController(View):

Лично я предпочитаю разместить мои декораторы поверх определенного метода, к которому они будут применяться:

class EMController(View):
    @method_decorator(decorators)
    def get(self, request, phone_number, event_id):
        ...

    @method_decorator(decorators)    
    def post(self, request, phone_number, event_id):
        ...