将 self 作为参数传递给方法装饰器配置器

问题描述 投票:0回答:2

我正在使用 DRF 和 swagger UI 文档制作一个 API, 我想将基本的 CRUD 操作作为动态视图集类,这样我就可以将类用于另一个模型, 但我有问题。我使用 @swagger_auto_schema 和 @action 作为装饰器。这里的例子 对于我获取所有数据表:

#dynamic_crud.py
class DynamicCrud(viewsets.ViewSet):
    
    def __init__(self, serializer_class, model_class, table_name):
        self.serializer_class = serializer_class
        self.model_class = model_class
        self.table_name = table_name

    
    def api_response(self, message, status, data=None):
        return {'message': message, 'status': status, 'data': data}
    
#here is the problem
    @swagger_auto_schema(
        operation_summary=f'Get all {table_name}',
        responses={200: serializer_class(many=True)}
    )
    @action(detail=False, methods=['get'], url_path='getall', url_name=f'{table_name}_getall')
    def get_all(self, request, *args, **kwargs):
        try:
            snippets = self.model_class.objects.filter(is_deleted=0)
            serializer = self.serializer_class(snippets, many=True)
            response_data = self.api_response(f'{self.table_name} retrieved successfully', 'success', serializer.data)
            return Response(response_data, status=status.HTTP_200_OK)
        except Exception as e:
            response_data = self.api_response(str(e), 'error')
            return Response(response_data, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

我将装饰器放在类中,装饰器需要引用数据。这个类是父类,数据引用在子类中。

#views.py
table_data = [
    {
        'serializer_class': serializer.TblMCategories_serializer,
        'model_class': models.TblMCategories,
        'table_name': 'category'
    },    
    {
        'serializer_class': serializer.TblMProduct_serializer,
        'model_class': models.TblMProduct,
        'table_name': 'product'
    },
    # {
    #   rest of my table data...
    # }
]

class TblMCategories(dynamic_crud.DynamicCrud):

    def __init__(self):
        self.serializer_class = table_data[0]['serializer_class']
        self.model_class = table_data[0]['model_class']
        self.table_name = table_data[0]['table_name']

我知道我不能在装饰器中使用类自数据,因为它尚未声明或自数据在代码构建期间不可用。有没有什么方法可以将数据作为 self 放入装饰器中,或者使我的装饰器具有所需的引用数据?

python database oop python-decorators
2个回答
0
投票

由于

self
仅在实际调用装饰方法时才可用,因此这里的技巧是避免在类定义时装饰该方法。

相反,您可以创建一个临时装饰器,在调用方法时使用

swagger_auto_schema
动态装饰给定方法,以便在装饰方法之前可以使用从
swagger_auto_schema
派生的参数传递
self

改变:

@swagger_auto_schema(
    operation_summary=f'Get all {table_name}',
    responses={200: serializer_class(many=True)}
)

至:

@(lambda method:
    lambda self, *args, **kwargs:
        swagger_auto_schema(
            operation_summary=f'Get all {self.table_name}',
            responses={200: serializer_class(many=True)}
        )(method)(self, *args, **kwargs)
)

这同样适用于

action

改变:

@action(detail=False, methods=['get'], url_path='getall', url_name=f'{table_name}_getall')

至:

@(lambda method:
    lambda self, *args, **kwargs:
        action(
            detail=False,
            methods=['get'],
            url_path='getall',
            url_name=f'{self.table_name}_getall'
        )(method)(self, *args, **kwargs)
)

最终结果是如下代码:

class DynamicCrud(viewsets.ViewSet):
    @(lambda method:
        lambda self, *args, **kwargs:
            swagger_auto_schema(
                operation_summary=f'Get all {self.table_name}',
                responses={200: serializer_class(many=True)}
            )(method)(self, *args, **kwargs)
    )
    @(lambda method:
        lambda self, *args, **kwargs:
            action(
                detail=False,
                methods=['get'],
                url_path='getall',
                url_name=f'{self.table_name}_getall'
            )(method)(self, *args, **kwargs)
    )
    def get_all(self, request, *args, **kwargs):
        ...

0
投票

我怎么会忘记OOP的基础,解决方案很简单。我不应该将数据发送回父类作为参考。相反,我将函数继承给子类,并在子类 python 文件中有我需要的引用数据的地方运行装饰器。

table_data = [
{
    'serializer_class': serializer.TblMCategories_serializer,
    'model_class': models.TblMCategories,
    'table_name': 'category'
},
# rest my table references data
]

class TblMCategories(dynamic_crud_viewset.DynamicCrud):

def __init__(self):
    super().__init__(
        serializer_class=table_data[0]['serializer_class'],
        model_class=table_data[0]['model_class'],
        table_name=table_data[0]['table_name']
    )

#run decorator in child class
@swagger_auto_schema(
    operation_summary=f'Get {table_data[0]["table_name"]} by ID',
    responses={200: serializer.TblMCategories_serializer()}
)
@action(detail=True, methods=['get'], url_path='getbyid',url_name=f'{table_data[0]["table_name"]}_getbyid')
def get_all(self, request, *args, **kwargs):
    return super().get_all(request, *args, **kwargs)
© www.soinside.com 2019 - 2024. All rights reserved.