Проверить имя параметра метода просмотра в представлениях на основе класса 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
который может дать ответ на вашу проблему:
Если вы хотите передать декоратор с параметрами, вам нужно только:
Вышеупомянутый и код, приведенный в связанном ответе, который был рассмотрен, вы должны:
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):
...