【问题标题】:Check view method parameter name in Django class based views检查基于 Django 类的视图中的视图方法参数名称
【发布时间】:2017-10-08 19:41:13
【问题描述】:

我在我的 Django 项目中创建了一个装饰器,用于将参数值注入到装饰方法的参数中。

我通过使用inspect.getargspec 来检查方法中存在哪些参数并将它们放在kwargs 中。否则,由于方法中的参数数量不正确,我会收到错误。

虽然这在单个视图方法中可以正常工作,但在涉及 Django 的基于类的视图时会失败。

我相信这可能是因为装饰器是在类级别使用 @method_decorator 应用到 dispatch 方法而不是单独的 getpost 方法。

我是 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 配置中应用装饰器或将装饰器应用到调度方法。

编辑: 我发布了与我正在探索的解决方法相关的代码,将参数名称作为参数传递给装饰器。

【问题讨论】:

  • 贴出你遇到问题的代码
  • 我认为它没有多大用处。但可以肯定...
  • 我试过你的代码,效果很好!那么,你能详细说明它失败的原因吗?
  • 好吧,根据测试场景它可能已经工作了,但是我正在尝试解决这个问题 :) 请更详细地阅读这个问题。如果其中有任何不清楚的地方,请告诉我。

标签: python django django-class-based-views


【解决方案1】:

我发现了这个帖子:Function decorators with parameters on a class based view in Django

这可能会为您的问题提供答案:

如果要传递带参数的装饰器,只需要:

  • 在 decorator-creator 函数中计算参数。

  • 将评估值传递给@method_decorator

考虑到上述内容和链接答案中提供的代码,您应该:

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

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


出于遗留原因将我之前的错误答案留在这里,不要在家里(或任何地方,在 django 中,就此而言!!)

如果我们观察"decorating a class" 文档,我们可以看到以下内容:

或者,更简洁地说,您可以改为装饰类并将要装饰的方法的名称作为关键字参数名称传递:

因此您必须更改 @method_decoratorname 参数以匹配将应用于的方法:

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

【讨论】:

  • 感谢您的回答,但是它不适用于 Django 基于类的视图。您不能在方法级别应用装饰器。它们需要应用于dispatch 方法。
  • @Thihara 我通过翻找一个可能的答案,我已经编辑了我的答案,看看!
  • 将参数传递给装饰器也是我最后的手段,但我更喜欢检测参数名称的黑魔法方式:)
  • 最终决定采用不同的方法。不过你可以得到赏金:)
【解决方案2】:

在图书馆的当前状态下,我想要的似乎是不可能的。所以这就是我最终选择的。

            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.GETrequest.POST

【讨论】:

    猜你喜欢
    • 2015-04-30
    • 1970-01-01
    • 2014-02-16
    • 2016-02-02
    • 2018-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-14
    相关资源
    最近更新 更多