如何将相同的装饰链应用于多个功能

发布于 2025-01-31 09:40:07 字数 2145 浏览 2 评论 0原文

@extend_schema(
    methods=['GET'],
    responses={(200, STYLES_MIME_TYPE): OpenApiTypes.BINARY})
@extend_schema(
    methods=['PUT'],
    request={STYLES_MIME_TYPE: OpenApiTypes.BINARY},
    responses={(204, 'application/json'): OpenApiResponse(
        response={'type': 'array', 'items': {'type': 'integer', 'format': 'int32'}},
        examples=[OpenApiExample(
            'Returned style IDs example',
            status_codes=['204'],
            value=[101, 102, 103])])})

@api_view(['GET', 'PUT'])
@permission_classes([IsAuthenticated|ReadOnly])
@renderer_classes([StylesRenderer, StylesJSONRenderer])
@parser_classes([StylesParser])
def styles(request: Request, pid: int) -> Response:
    """
    Get or save styles for a project.
    GET - protobuf binary response
    POST - returnd IDs for saved styles
    """
    try:
        project = Project.objects.get(pk=pid)
        return _handle_styles(request, project)
    except Project.DoesNotExist:
        raise Http404()


@extend_schema(
    methods=['GET'],
    responses={(200, STYLES_MIME_TYPE): OpenApiTypes.BINARY})
@extend_schema(
    methods=['PUT'],
    request={STYLES_MIME_TYPE: OpenApiTypes.BINARY},
    responses={(204, 'application/json'): OpenApiResponse(
        response={'type': 'array', 'items': {'type': 'integer', 'format': 'int32'}},
        examples=[OpenApiExample(
            'Returned style IDs example',
            status_codes=['204'],
            value=[101, 102, 103])])})

@api_view(['GET', 'PUT'])
@permission_classes([IsAuthenticated|ReadOnly])
@renderer_classes([StylesRenderer, StylesJSONRenderer])
@parser_classes([StylesParser])
def styles_xref(request: Request, xref: uuid.UUID) -> Response:
    """
    Get or save styles for a project.
    GET - protobuf binary response
    POST - returnd IDs for saved styles
    """
    try:
        project = Project.objects.get(xref=xref)
        return _handle_styles(request, project)
    except Project.DoesNotExist:
        raise Http404()

这是django,显然我想为这2个视图使用相同的装饰器。唯一的区别是,一个通过int ID查找对象,另一个通过UUID XREF字段查找对象。我该如何保持干燥?

@extend_schema(
    methods=['GET'],
    responses={(200, STYLES_MIME_TYPE): OpenApiTypes.BINARY})
@extend_schema(
    methods=['PUT'],
    request={STYLES_MIME_TYPE: OpenApiTypes.BINARY},
    responses={(204, 'application/json'): OpenApiResponse(
        response={'type': 'array', 'items': {'type': 'integer', 'format': 'int32'}},
        examples=[OpenApiExample(
            'Returned style IDs example',
            status_codes=['204'],
            value=[101, 102, 103])])})

@api_view(['GET', 'PUT'])
@permission_classes([IsAuthenticated|ReadOnly])
@renderer_classes([StylesRenderer, StylesJSONRenderer])
@parser_classes([StylesParser])
def styles(request: Request, pid: int) -> Response:
    """
    Get or save styles for a project.
    GET - protobuf binary response
    POST - returnd IDs for saved styles
    """
    try:
        project = Project.objects.get(pk=pid)
        return _handle_styles(request, project)
    except Project.DoesNotExist:
        raise Http404()


@extend_schema(
    methods=['GET'],
    responses={(200, STYLES_MIME_TYPE): OpenApiTypes.BINARY})
@extend_schema(
    methods=['PUT'],
    request={STYLES_MIME_TYPE: OpenApiTypes.BINARY},
    responses={(204, 'application/json'): OpenApiResponse(
        response={'type': 'array', 'items': {'type': 'integer', 'format': 'int32'}},
        examples=[OpenApiExample(
            'Returned style IDs example',
            status_codes=['204'],
            value=[101, 102, 103])])})

@api_view(['GET', 'PUT'])
@permission_classes([IsAuthenticated|ReadOnly])
@renderer_classes([StylesRenderer, StylesJSONRenderer])
@parser_classes([StylesParser])
def styles_xref(request: Request, xref: uuid.UUID) -> Response:
    """
    Get or save styles for a project.
    GET - protobuf binary response
    POST - returnd IDs for saved styles
    """
    try:
        project = Project.objects.get(xref=xref)
        return _handle_styles(request, project)
    except Project.DoesNotExist:
        raise Http404()

This is Django, and obviously I want to use the same decorators for those 2 views. The only difference is that one looks up object by int ID, and the other by UUID xref field. How can I keep this DRY?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

剩余の解释 2025-02-07 09:40:07

您可以定义一个新的装饰器,该装饰符可通过所需的链条返回预装的功能。例如,我们首先可以定义三个自定义装饰器:

import functools


# A decorator factory which returns a new decorator.
def decorator_factory(message):

    def decorator(function):

        # Wraps the decorated function.
        @functools.wraps(function)
        def wrapper(*args, **kwargs):

            # Example behavior:
            #  - Prints a message before calling the decorated function.
            print(message)

            # Calls the decorated function.
            return function(*args, **kwargs)

        return wrapper

    return decorator


# Defines three new decorators.
decorator_1 = decorator_factory("Ham")
decorator_2 = decorator_factory("Spam")
decorator_3 = decorator_factory("Eggs")

这些装饰器目前的调用方式类似于以下内容,这很快就会重复多个功能:

@decorator_1
@decorator_2
@decorator_3
def f():

    pass  # Do something.


@decorator_1
@decorator_2
@decorator_3
def g():

    pass  # Do something.


@decorator_1
@decorator_2
@decorator_3
def h():

    pass  # Do something.

但是,您可以在装饰器正文中装饰包装器功能:

def decorator_chain(function):

    @functools.wraps(function)
    @decorator_1
    @decorator_2
    @decorator_3
    def wrapper(*args, **kwargs):

        return function(*args, **kwargs)

    return wrapper

简化功能定义的功能定义。 to:

@decorator_chain
def f():

    pass  # Do something.


@decorator_chain
def g():

    pass  # Do something.


@decorator_chain
def h():

    pass  # Do something.

在您提供的示例中,这可能看起来像以下内容:

import functools


def decorator_chain(function):

    @functools.wraps(function)
    @extend_schema(
        methods   = ['GET'],
        responses = {(200, STYLES_MIME_TYPE): OpenApiTypes.BINARY}
    )
    @extend_schema(
        methods   = ['PUT'],
        request   = {STYLES_MIME_TYPE: OpenApiTypes.BINARY},
        responses = {
            (204, 'application/json'): OpenApiResponse(
                response = {'type': 'array', 'items': {'type': 'integer', 'format': 'int32'}},
                examples = [
                    OpenApiExample(
                        'Returned style IDs example',
                        status_codes = ['204'],
                        value        = [101, 102, 103]
                    )
                ]
            )
        }
    )
    @api_view(['GET', 'PUT'])
    @permission_classes([IsAuthenticated | ReadOnly])
    @renderer_classes([StylesRenderer, StylesJSONRenderer])
    @parser_classes([StylesParser])
    def wrapper(*args, **kwargs):

        return function(*args, **kwargs)

    return wrapper


@decorator_chain
def styles(request: Request, pid: int) -> Response:
    """
    Get or save styles for a project.
    GET - protobuf binary response
    POST - returnd IDs for saved styles
    """
    try:
        project = Project.objects.get(pk=pid)
        return _handle_styles(request, project)
    except Project.DoesNotExist:
        raise Http404()


@decorator_chain
def styles_xref(request: Request, xref: uuid.UUID) -> Response:
    """
    Get or save styles for a project.
    GET - protobuf binary response
    POST - returnd IDs for saved styles
    """
    try:
        project = Project.objects.get(xref=xref)
        return _handle_styles(request, project)
    except Project.DoesNotExist:
        raise Http404()

使用装饰工厂甚至可以让您快速创建给定的装饰链的不同变体。

You could define a new decorator which returns a pre-decorated function with the chain you want. For example, we can first define three custom decorators:

import functools


# A decorator factory which returns a new decorator.
def decorator_factory(message):

    def decorator(function):

        # Wraps the decorated function.
        @functools.wraps(function)
        def wrapper(*args, **kwargs):

            # Example behavior:
            #  - Prints a message before calling the decorated function.
            print(message)

            # Calls the decorated function.
            return function(*args, **kwargs)

        return wrapper

    return decorator


# Defines three new decorators.
decorator_1 = decorator_factory("Ham")
decorator_2 = decorator_factory("Spam")
decorator_3 = decorator_factory("Eggs")

The way these decorators are presently invoked resembles the following, which quickly becomes repetitive for multiple functions:

@decorator_1
@decorator_2
@decorator_3
def f():

    pass  # Do something.


@decorator_1
@decorator_2
@decorator_3
def g():

    pass  # Do something.


@decorator_1
@decorator_2
@decorator_3
def h():

    pass  # Do something.

However, you can decorate a wrapper function within the body of a decorator:

def decorator_chain(function):

    @functools.wraps(function)
    @decorator_1
    @decorator_2
    @decorator_3
    def wrapper(*args, **kwargs):

        return function(*args, **kwargs)

    return wrapper

Which simplifies the function definitions to:

@decorator_chain
def f():

    pass  # Do something.


@decorator_chain
def g():

    pass  # Do something.


@decorator_chain
def h():

    pass  # Do something.

In your provided example, this might look something like the following:

import functools


def decorator_chain(function):

    @functools.wraps(function)
    @extend_schema(
        methods   = ['GET'],
        responses = {(200, STYLES_MIME_TYPE): OpenApiTypes.BINARY}
    )
    @extend_schema(
        methods   = ['PUT'],
        request   = {STYLES_MIME_TYPE: OpenApiTypes.BINARY},
        responses = {
            (204, 'application/json'): OpenApiResponse(
                response = {'type': 'array', 'items': {'type': 'integer', 'format': 'int32'}},
                examples = [
                    OpenApiExample(
                        'Returned style IDs example',
                        status_codes = ['204'],
                        value        = [101, 102, 103]
                    )
                ]
            )
        }
    )
    @api_view(['GET', 'PUT'])
    @permission_classes([IsAuthenticated | ReadOnly])
    @renderer_classes([StylesRenderer, StylesJSONRenderer])
    @parser_classes([StylesParser])
    def wrapper(*args, **kwargs):

        return function(*args, **kwargs)

    return wrapper


@decorator_chain
def styles(request: Request, pid: int) -> Response:
    """
    Get or save styles for a project.
    GET - protobuf binary response
    POST - returnd IDs for saved styles
    """
    try:
        project = Project.objects.get(pk=pid)
        return _handle_styles(request, project)
    except Project.DoesNotExist:
        raise Http404()


@decorator_chain
def styles_xref(request: Request, xref: uuid.UUID) -> Response:
    """
    Get or save styles for a project.
    GET - protobuf binary response
    POST - returnd IDs for saved styles
    """
    try:
        project = Project.objects.get(xref=xref)
        return _handle_styles(request, project)
    except Project.DoesNotExist:
        raise Http404()

Using a decorator factory could even allow you to quickly create different variants of a given chain of decorators.

幸福还没到 2025-02-07 09:40:07

修改了 geeksforgeeks

装饰器本质上是普通功能。因此,您可以使用部分方法向他们提供一组参数,而无需实际调用该方法。

from functools import partial
def decorator(like):
    print("Inside decorator")
    def inner(func):
        print(like)
        func()
    return inner

premade_decorator = partial(decorator, like = "geeksforgeeks")
 
@premade_decorator()
def my_func():
    print("Inside actual function")

现在,我们知道如何将参数预先贴到装饰器上,我们可以尝试将它们链接在一起:

def chain_decorator(dec_list):
    def inner(func):
        for dec in dec_list:
            func = partial(dec(), func)
        return func
    return inner
 
@chain_decorator([premade_decorator1, premade_decorator2])
def my_func():
    print("Inside actual function")
    return 1

print(my_func())

Modified example from GeeksForGeeks

Decorators are, in essence, ordinary functions. Thus, you can use partial method to provide a set of arguments to them without actually calling the method.

from functools import partial
def decorator(like):
    print("Inside decorator")
    def inner(func):
        print(like)
        func()
    return inner

premade_decorator = partial(decorator, like = "geeksforgeeks")
 
@premade_decorator()
def my_func():
    print("Inside actual function")

Now we know how to pre-apply parameters to a decorator, we can try chaining them:

def chain_decorator(dec_list):
    def inner(func):
        for dec in dec_list:
            func = partial(dec(), func)
        return func
    return inner
 
@chain_decorator([premade_decorator1, premade_decorator2])
def my_func():
    print("Inside actual function")
    return 1

print(my_func())
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文