在Django中是否可以将命令行参数传递给装饰器?

qltillow  于 2022-12-01  发布在  Go
关注(0)|答案(3)|浏览(140)

我有一个装饰器,它应该使用从命令行传入的参数,例如
第一个
对此有什么建议吗?

gkl3eglg

gkl3eglg1#

您可以创建一个“元装饰器”,类似于:

from functool import wraps

def metadeco(function):
    @wraps(function)
    def func(*args, **kwargs):
        name = kwargs['name']
        return deco(name)(function)(*args, **kwargs)
    return func

然后使用元装饰器:

class Command(BaseCommand):
    def add_arguments(self, parser):
        parser.add_argument(
            "--name",
            type=str,
            required=True,
        )
    
    @metadeco
    def handle(self, *_args, **options):
        name = options['name']
        # …
xmjla07d

xmjla07d2#

当应用@deco装饰器时,您没有访问命令行值的权限,没有。但是您 * 可以 * 延迟应用该装饰器,直到您有访问权限为止。
通过创建你自己的装饰器来实现这一点。装饰器只是一个函数,当Python解析@decoratordef functionname行时,就在Python创建函数对象之后应用它;装饰器的返回值取代了被装饰的函数。那么,你需要确保的是,当命令被执行时,你的装饰器返回一个不同的函数来应用deco装饰器**。
下面是这样一个装饰者:

from functools import wraps

def apply_deco_from_name(f):
    @wraps(f)
    def wrapper(self, *args, **options):
        # this code is called instead of the decorated method
        # and *now* we have access to the options mapping.
        name = options["name"]  # or use options.pop("name") to remove it
        decorated = deco(name)(f)  # the same thing as @deco(name) for the function
        return decorated(self, *args, **options)
        
    return wrapper

然后在命令处理程序上使用该装饰器:

class Command(BaseCommand):
    def add_arguments(self, parser):
        parser.add_argument(
            "--name",
            type=str,
            required=True,
        )

    @apply_deco_from_name
    def handle(self, *_args, **options):
        name = options["name"]

这里发生了什么?当Python处理@apply_deco_from_namedef handle(...)行时,它将其视为类内部的一个完整的函数语句。它将创建一个handle函数对象,然后将其传递给装饰器,因此它调用apply_deco_from_name(handle)。上面定义的装饰器返回wrapper * 而不是 *。
当Django执行这个命令处理程序时,它会调用wrapper(command, [other arguments], name="command-line-value-for-name", [other options])来替换它。此时代码会创建一个新的修饰版本的处理程序,它使用decorated = deco("command-line-value-for-name")(f) *,就像Python在命令类中使用@deco("command-line-value-for-name") * 一样。deco("command-line-value-for-name")返回一个装饰器函数,deco("command-line-value-for-name")(f)返回一个 Package 器。并在最后调用 Package 器。

oyxsuwqo

oyxsuwqo3#

你的装饰器并不一定是一个装饰器,因为你使用的是类,你可以使用mixin pattern

class YourMixin:
    def handle(self, name):
        # Code that was previously in deco

class Command(YourMixin, BaseCommand):
    def add_arguments(self, parser):
        parser.add_argument(
            "--name",
            type=str,
            required=True,
        )

    def handle(self, *_args, **options):
        # Code before calling YourMixin.handle
        name = options["name"]
        super().handle(name)
        # Code after calling YourMixin.handle

相关问题